import APIClient
import ComponentLibrary
import ComposableArchitecture
import FeatureAnnouncements
import FeatureToasts
import Localization
import Popovers
import SwiftUI
import Utilities

/*
 Orpheus Custom Create sheet with:
 - header with credits
 - audio upload/cover player/extend player section
 - styles
 - lyrics
 - advanced options
 - title
 
 This order is configured based on the Prompt that's passed in
 to the reducer. For example, Extend always shows lyrics before styles.
*/
public struct OrpheusCustomCreateSheetView: View {
    @Bindable var store: StoreOf<OrpheusCustomCreate>
    @FocusState var focusedField: OrpheusCustomCreate.State.Field?

    /// Only for testing purposes when displaying in a regular SwiftUi `.sheet()` for RootTabCoordinator
    // TODO: (Asad) Remove after done testing
    let isNestedInCustomSheet: Bool

    @State private var keyboardHeight: CGFloat = 0

    public init(
        store: StoreOf<OrpheusCustomCreate>,
        isNestedInCustomSheet: Bool = true
    ) {
        self.store = store
        self.isNestedInCustomSheet = isNestedInCustomSheet
    }

    private var hasStyles: Bool {
        !store.styles.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
    }

    private var lyricsTextBinding: Binding<String> {
        Binding<String>(
            get: {
                store.instrumental ? L10n.FeatureEditClip.lyricsPlaceholderInstrumental : store.lyrics
            },
            set: { newValue in
                store.lyrics = newValue
            }
        )
    }
    
    private var lyricsPlaceholder: String {
        if case .remix = store.mode, store.remixMode == .extend {
            if let sourceClip = store.sourceClip {
                let timestamp = store.extendTimestamp
                return L10n.FeatureCreateClip.continueSongFrom(formatTimeForExtension(timestamp))
            }
            return L10n.FeatureCreateClip.continueSong
        }
        return L10n.FeatureEditClip.lyricsPlaceholder
    }
    
    private func formatTimeForExtension(_ seconds: Double) -> String {
        let minutes = Int(seconds) / 60
        let remainingSeconds = Int(seconds) % 60
        return String(format: "%d:%02ds", minutes, remainingSeconds)
    }

    private var scrollAnchor: UnitPoint {
        keyboardHeight > 0 ? UnitPoint(x: 0.5, y: 0.35) : UnitPoint(x: 0.5, y: 0.5)
    }

    private var bottomSpacing: CGFloat {
        keyboardHeight > 0 ? keyboardHeight : 80
    }

    public var body: some View {
        VStack(spacing: 0) {
            if !isNestedInCustomSheet {
                // Only show grabber when NOT nested in CustomSheet
                // (CustomSheet has its own grabber)
                grabber
            }
            header
                .padding(.bottom, 8)

            content
        }
        .safeAreaInset(edge: .bottom) {
            createFooter
        }
        .bind($store.focusedField, to: $focusedField)
        .task {
            store.send(.task)
        }
        .onDisappear {
            store.send(.teardown)
        }
        .presentationDragIndicator(.hidden)
        .presentationContentInteraction(.scrolls)
        .modify {
            /// Use default styling on iOS 26+
            if #available(iOS 26.0, *) {
                $0
            } else {
                $0.presentationBackground {
                    Color.FigmaMCP.Semantic.smokeDense
                        .background(.ultraThinMaterial)
                }
            }
        }
        .preferredColorScheme(.dark)
        .onTapGesture { self.dismissKeyboard() }
        .overlay(alignment: .top) {
            ToastView(store: store.scope(state: \.toast, action: \.toast)) { destination in
                if destination == .paywall, store.me.flags[FlagKey.iosSubscriptions] == true {
                    store.send(.showSubscriptions)
                }
            }
        }
        .alert($store.scope(state: \.destination?.alert, action: \.destination.alert))
        .overlay {
            if let store = store.scope(state: \.destination?.bluejayAnnouncement, action: \.destination.bluejayAnnouncement) {
                BluejayAnnouncementView(store: store)
            }
        }
        .overlay {
            if let store = store.scope(state: \.destination?.v5Announcement, action: \.destination.v5Announcement) {
                V5AnnouncementView(store: store)
            }
        }
    }

    private var grabber: some View {
        VStack {
            RoundedRectangle(cornerRadius: 100)
                .fill(Color.FigmaMCP.Semantic.fogDense)
                .frame(width: 36, height: 5)
        }
        .padding(.top, 6)
        .padding(.bottom, 16)
    }

    private var header: some View {
        HStack {
            VStack(alignment: .leading, spacing: 2) {
                Text(L10n.FeatureCreateClip.custom)
                    .figmaMCPTypography(TypographyV1.FigmaMCP.mediumTitle)
                    .foregroundColor(Color.FigmaMCP.Semantic.foregroundPrimary)
                    .tracking(0.32)

                if let billingInfo = store.billingInfo {
                    Text(L10n.FeatureCreateClip.creditsCount("\(billingInfo.totalCreditsLeft)"))
                        .figmaMCPTypography(TypographyV1.FigmaMCP.xSmallRegular)
                        .foregroundColor(billingInfo.totalCreditsLeft <= 0 ? Color.FigmaMCP.Semantic.accentError : Color.FigmaMCP.Semantic.foregroundTertiary)
                        .tracking(0.24)
                } else {
                    Text(L10n.FeatureCreateClip.creditsPlaceholder)
                        .figmaMCPTypography(TypographyV1.FigmaMCP.xSmallRegular)
                        .foregroundColor(Color.FigmaMCP.Semantic.foregroundTertiary)
                        .tracking(0.24)
                }
            }

            Spacer()

            ModelSelectorV3(
                selectedModel: store.selectedSunoModel,
                models: store.availableModels,
                setSelectedModel: { model in
                    if model.canUse == false {
                        store.send(.showUpgradeAlert(model.marketingLevelUnderstanding))
                    } else {
                        store.send(.didSelectModel(model))
                    }
                }
            )
            .environment(\.colorScheme, .dark)
        }
        .padding(.horizontal, 16)
    }
    
    @ViewBuilder
    private var content: some View {
        ScrollViewReader { proxy in
            ScrollViewWithSheetSupport {
                VStack(spacing: 16) {
                    ForEach(Array(store.sectionOrder.enumerated()), id: \.offset) { _, sectionType in
                        switch sectionType {
                        case .modular:
                            modularSection
                        case .styles:
                            styleSection
                                .id(OrpheusCustomCreate.State.ScrollTarget.styles)
                        case .lyrics:
                            lyricsSection
                                .id(OrpheusCustomCreate.State.ScrollTarget.lyrics)
                        case .advancedOptions:
                            if store.showSliders {
                                advancedOptionsSection
                                    .id(OrpheusCustomCreate.State.ScrollTarget.advancedOptions)
                            }
                        case .title:
                            songTitleSection
                                .id(OrpheusCustomCreate.State.ScrollTarget.title)
                        }
                    }

                    Spacer()
                        .frame(height: bottomSpacing)
                }
                .padding([.horizontal, .bottom], 16)
                .padding(.top, 8)
            }
            .scrollDismissesKeyboard(.interactively)
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .trackKeyboardHeight($keyboardHeight)
            .onChange(of: focusedField) { _, newValue in
                guard let field = newValue else { return }
                scrollToField(field, proxy: proxy)
            }
            .onChange(of: store.scrollTarget) { _, target in
                guard let target = target else { return }
                scrollToTarget(target, proxy: proxy)
            }
        }
    }

    @ViewBuilder
    private var modularSection: some View {
        switch store.mode {
        case .default:
            defaultModeSection
        case .remix:
            if store.remixMode == .cover {
                coverModeSection
            } else {
                extendModeSection
            }
        }
    }

    @ViewBuilder
    private var defaultModeSection: some View {
        Button(action: {
//            store.send(.addAudio) TODO: Add Audio upload flows
        }) {
            HStack(spacing: 8) {
                Image.FigmaMCP.plus
                    .figmaMCPIconStyle(size: 16, semanticColor: Color.FigmaMCP.Semantic.foregroundPrimary)
                
                Text(L10n.FeatureCreateClip.audioButton)
                    .figmaMCPTypography(TypographyV1.FigmaMCP.small)
                    .foregroundColor(Color.FigmaMCP.Semantic.foregroundPrimary)
                    .tracking(0.28)
            }
            .frame(maxWidth: .infinity)
            .frame(height: 56)
            .background(Color.FigmaMCP.Semantic.fogThin)
        }
        .buttonStyle(PlainButtonStyle())
        .clipShape(RoundedRectangle(cornerRadius: 16))
    }

    @ViewBuilder
    private var coverModeSection: some View {
        if let audioPlayerStore = store.scope(state: \.audioPlayerSection, action: \.audioPlayerSection),
           store.sourceClip != nil {
            CustomCreatePlayerView(
                store: audioPlayerStore,
                sectionType: .cover
            )
        } else {
            EmptyView()
        }
    }
    
    @ViewBuilder
    private var extendModeSection: some View {
        if let audioPlayerStore = store.scope(state: \.audioPlayerSection, action: \.audioPlayerSection),
           let sourceClip = store.sourceClip {
            CustomCreatePlayerView(
                store: audioPlayerStore,
                sectionType: .extend(
                    extendTimestamp: store.extendTimestamp,
                    clipDuration: sourceClip.duration,
                    onExtendTimestampChanged: { newTimestamp in
                        store.send(.binding(.set(\.extendTimestamp, newTimestamp)))
                    }
                )
            )
        } else {
            EmptyView()
        }
    }

    private var styleSection: some View {
        ExpandableSection(
            title: L10n.FeatureCreateClip.style,
            text: $store.styles,
            limit: store.stylesCharCountLimit,
            placeholder: L10n.FeatureCreateClip.stylePlaceholder,
            focusBinding: $focusedField,
            focusValue: .styles
        ) {
            if !store.recommendedStyles.isEmpty {
                ScrollView(.horizontal, showsIndicators: false) {
                    HStack(spacing: 8) {
                        ForEach(store.recommendedStyles, id: \.self) { style in
                            Button(action: {
                                store.send(.selectStyle(style))
                            }) {
                                HStack(spacing: 4) {
                                    Image.FigmaMCP.plus
                                        .figmaMCPIconStyle(size: 16, semanticColor: Color.FigmaMCP.Semantic.foregroundPrimary)

                                    Text(style)
                                        .figmaMCPTypography(TypographyV1.FigmaMCP.xSmallTitle)
                                        .foregroundColor(Color.FigmaMCP.Semantic.foregroundPrimary)
                                        .tracking(0.24)
                                        .lineLimit(1)
                                }
                                .padding(.horizontal, 16)
                            }
                            .buttonStyle(PlainButtonStyle())
                            .frame(height: 40)
                            .background(Color.FigmaMCP.Semantic.fogThin)
                            .clipShape(RoundedRectangle(cornerRadius: 100))
                        }
                    }
                    .padding(.horizontal, 16)
                }
                .scrollClipDisabled()
                .scrollBounceBehavior(.basedOnSize)
                .padding(.bottom, 16)
            }
        }
    }

    private var lyricsSection: some View {
        ExpandableSection(
            title: L10n.FeatureEditClip.lyrics,
            text: lyricsTextBinding,
            limit: store.lyricsCharCountLimit,
            placeholder: lyricsPlaceholder,
            focusBinding: $focusedField,
            focusValue: .lyrics,
            isDisabled: store.instrumental
        ) {
            Button(action: {
                store.send(.binding(.set(\.instrumental, !store.instrumental)), animation: .easeInOut(duration: 0.2))
            }) {
                HStack(spacing: 4) {
                    Image.FigmaMCP.success
                        .figmaMCPIconStyle(size: 16, semanticColor: store.instrumental ? Color.FigmaMCP.Semantic.accentBrand : Color.FigmaMCP.Semantic.fogDense)

                    Text(L10n.FeatureCreateClip.instrumental)
                        .figmaMCPTypography(TypographyV1.FigmaMCP.xSmallTitle)
                        .foregroundColor(store.instrumental ? Color.FigmaMCP.Semantic.backgroundPrimary : Color.FigmaMCP.Semantic.foregroundPrimary)
                        .tracking(0.24)
                }
                .padding(.horizontal, 12)
                .padding(.vertical, 8)
                .frame(height: 40)
                .background(
                    RoundedRectangle(cornerRadius: 100)
                        .fill(store.instrumental ? Color.FigmaMCP.Semantic.foregroundPrimary : Color.clear)
                        .overlay(
                            RoundedRectangle(cornerRadius: 100)
                                .stroke(Color.FigmaMCP.Semantic.borderPrimary, lineWidth: 1)
                        )
                )
            }
            .buttonStyle(PlainButtonStyle())
            .padding(.horizontal, 16)
            .padding(.bottom, 16)
        }
    }
    
    private var advancedOptionsSection: some View {
        let config: AdvancedOptionsConfig = {
            switch store.mode {
            case .remix:
                return .remixOnly
            case .default:
                return .default
            }
        }()
        
        return AdvancedOptionsSection(store: store, focusedField: $focusedField, config: config)
    }
    
    private var songTitleSection: some View {
        SongTitleField(
            title: $store.title,
            characterLimit: store.titleCharCountLimit,
            focusedField: $focusedField,
            focusValue: OrpheusCustomCreate.State.Field.title
        )
    }

    private var createFooter: some View {
        AuraButton(
            title: L10n.FeatureEditClip.create,
            isLoading: store.isSubmitting,
            auraStyle: .orangeAura,
            withGradientOverlay: false,
            leadingView: {
                Image.FigmaMCP.create
                    .figmaMCPIconStyle(size: 16, semanticColor: Color.FigmaMCP.Semantic.foregroundPrimary)
            },
            action: { 
                store.send(.createTapped)
            }
        )
        .pillButtonSizeV1(.createButtonSize)
        .padding(.horizontal, 16)
        .padding(.bottom, 8)
        .opacity(hasStyles ? 1 : 0.6)
        .disabled(!hasStyles)
    }
    
    private func dismissKeyboard() {
        focusedField = nil
    }
}

extension PillButtonSizeV1 {
    public static let createButtonSize = PillButtonSizeV1(
        typography: .button1,
        width: nil,
        maxWidth: .infinity,
        minHeight: 60,
        iconWidth: 24,
        borderRadius: 22,
        padding: .init(top: 0, leading: 24, bottom: 0, trailing: 24)
    )
}

// MARK: - Scroll Helpers

private extension OrpheusCustomCreateSheetView {
    func scrollToField(_ field: OrpheusCustomCreate.State.Field, proxy: ScrollViewProxy) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            withAnimation(.easeInOut(duration: 0.3)) {
                let scrollTarget: OrpheusCustomCreate.State.ScrollTarget = {
                    switch field {
                    case .styles: return .styles
                    case .lyrics: return .lyrics
                    case .title: return .title
                    case .excludeStyles: return .advancedOptions
                    }
                }()
                proxy.scrollTo(scrollTarget, anchor: scrollAnchor)
            }
        }
    }

    func scrollToTarget(_ target: OrpheusCustomCreate.State.ScrollTarget, proxy: ScrollViewProxy) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            withAnimation(.easeInOut(duration: 0.2)) {
                proxy.scrollTo(target, anchor: scrollAnchor)
            }
            store.send(.binding(.set(\.scrollTarget, nil)))
        }
    }
}
