import Adamantium
import APIClient
import BackendEnvironmentClient
import ComponentLibrary
import ComposableArchitecture
import FeatureBrandedAlert
import FeatureToasts
import FeatureTopUp
import Foundation
import Localization
import NavigationRouterClient
import PaywallClient
import StatsigClient
import StoreKit
import SwiftUI
import Utilities

@Reducer
public struct PaywallLoadedV2 {
    @Reducer(state: .equatable)
    public enum Destination {
        case topUp(TopUp)
    }

    @ObservableState
    public struct State: Equatable {
        var isTopUpsEnabled: Bool {
            FeatureFlag.legacy.topUps
        }

        var annualPlanDiscountPercentage: Int? {
            let value = FeatureFlag.legacy.annualPlanDiscountPercentage
            guard value > 0 else { return nil }
            return value
        }

        var monthlyPlanDiscountPercentage: Int? {
            let value = FeatureFlag.legacy.monthlyPlanDiscountPercentage
            guard value > 0 else { return nil }
            return value
        }

        var isV5Launch: Bool {
            FeatureFlag.legacy.v5Launch == true
        }

        @Presents public var destination: Destination.State?
        let subscriptions: [Subscription]
        let appStoreCurrencyCode: String?
        var purchases: [Entitlement]
        var selectedSubscription: Subscription?
        var billingInfo: SubscriptionInfoResponse
        var purchasing: Subscription?
        let subscriptionPageContent: SubscriptionPageContentResponse
        var selectedPeriod: Subscription.Period = .year

        var showCancelAlert: Bool = false
        var shouldScrollToFeatures: Bool = false

        var columnTitles: [String] {
            [
                L10n.FeaturePaywall.freePlanColumn,
                L10n.FeaturePaywall.proPlanColumn,
                L10n.FeaturePaywall.premierPlanColumn
            ]
        }

        var items: [FeatureRowResponse] {
            subscriptionPageContent.featureComparison.features
        }

        var isSubscribed: Bool {
            return !purchases.isEmpty || billingInfo.plan != nil
        }

        var showLegacyWarning: Bool {
            return purchases.contains(where: { $0.isLegacy && !$0.unsubscribed } )
        }
        
        var showStripeInfo: Bool {
            return billingInfo.subscriptionPlatform == "stripe"
        }

        var currentSubscription: Subscription? {
            if let purchase = purchases.first {
                return subscriptions.first(where: {
                    $0.semanticPlan == purchase.plan && $0.period == purchase.period
                })
            }

            if billingInfo.subscriptionPlatform == "stripe",
               let plan = billingInfo.plan {
                let semanticPlan: Subscription.SemanticPlan = {
                    switch plan.level {
                    case 10: return .pro
                    case 30: return .premier
                    default: return .basic
                    }
                }()

                let period: Subscription.Period = {
                    switch billingInfo.period {
                    case "month": return .month
                    case "year", "annual": return .year
                    default: return .month
                    }
                }()

                return subscriptions.first(where: {
                    $0.semanticPlan == semanticPlan && $0.period == period
                })
            }

            return nil
        }

        func findEquivalentSubscription(for plan: Subscription.SemanticPlan, period: Subscription.Period) -> Subscription? {
            return subscriptions.first(where: {
                $0.semanticPlan == plan && $0.period == period
            })
        }
    }

    public enum Action {
        case destination(PresentationAction<Destination.Action>)

        case viewDidAppear

        case tapped(Subscription?)
        case purchase
        case purchaseResult(Result<(Subscription, [Entitlement]), Error>)
        case topUpTapped
        case delegate(Delegate)
        case openAppStoreSubscriptions
        case cancelTapped
        case dismissAlert
        case continueTapped
        case compareTapped
        case periodChanged(Subscription.Period)

        public enum Delegate {
            case refreshCreditsAfter
            case restore
        }
    }

    @Dependency(\.openURL) var openURL
    @Dependency(PaywallClient.self) private var paywall
    @Dependency(NavigationRouterClient.self) private var navigationRouter
    @Dependency(\.toastClient.show) var showToast

    private func showManageSubscriptions() async {
        guard let windowScene = await UIWindow.current?.windowScene else {
            log.telemetry.assertionFailure("Failed to get window scene for showing subscriptions")
            return
        }

        do {
            try await AppStore.showManageSubscriptions(in: windowScene)
        } catch {
            log.telemetry.error(error, message: "Failed to show manage subscriptions.")
        }
    }

    public var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .tapped(let subscription):
                state.selectedSubscription = subscription
                return .none

            case .purchase:
                guard let subscription = state.selectedSubscription else {
                    return .none
                }

                
                state.purchasing = subscription
                let isDowngrade = subscription.isDowngrade(from: state.currentSubscription)
                return .run { send in
                    await send(.purchaseResult(Result(catching: {
                        let entitlements = try await paywall.purchase(subscription, !isDowngrade)
                        return (subscription, entitlements)
                    })))
                }

                return .none

            case .purchaseResult(.success((_, let entitlements))):
                state.purchasing = nil
                state.purchases = entitlements
                return .none

            case .purchaseResult(.failure(PaywallError.cancelled)):
                state.purchasing = nil
                return .none

            case .purchaseResult(.failure(let error)):
                log.telemetry.error(error)
                return .none

            case .topUpTapped:
                navigationRouter.sendIfNavV2(route: .topUp, else: {
                    state.destination = .topUp(.init())
                })
                return .none

            case .destination(.presented(.topUp(.delegate(.toastAfter(let toast))))):
                showToast(toast)
                return .none

            case .destination(.presented(.topUp(.delegate(.refreshCreditsAfter)))):
                return .send(.delegate(.refreshCreditsAfter))

            case .destination(.presented(.topUp(.delegate(.setBillingInfoAfter(let billingInfo))))):
                state.billingInfo = billingInfo
                return .none

            case .destination, .delegate:
                return .none

            case .viewDidAppear:
                if state.selectedSubscription == nil {
                    if state.isSubscribed {
                        if let currentPlan = state.currentSubscription {
                            state.selectedSubscription = currentPlan
                        }
                    } else {
                        if let proSubscription = state.subscriptions.first(where: {
                            $0.semanticPlan == .pro && $0.period == .year
                        }) {
                            state.selectedSubscription = proSubscription
                            state.selectedPeriod = .year
                        }
                    }
                }
                return .none

            case .periodChanged(let newPeriod):
                state.selectedPeriod = newPeriod

                if let currentSelected = state.selectedSubscription {
                    if let equivalentPlan = state.findEquivalentSubscription(
                        for: currentSelected.semanticPlan,
                        period: newPeriod
                    ) {
                        state.selectedSubscription = equivalentPlan
                    }
                } else if !state.isSubscribed {
                    if let proSubscription = state.subscriptions.first(where: {
                        $0.semanticPlan == .pro && $0.period == newPeriod
                    }) {
                        state.selectedSubscription = proSubscription
                    }
                }
                return .none

            case .openAppStoreSubscriptions:
                return .run { send in
                    await showManageSubscriptions()
                    await send(.delegate(.restore))
                    await send(.destination(.dismiss))
                }

            case .cancelTapped:
                if state.billingInfo.subscriptionPlatform == "stripe" {
                    let baseUrl = BackendEnvironmentProvider.currentConfiguration().webEndpointHost
                    if let url = URL(string: "https://\(baseUrl)/account") {
                        return .run { _ in
                            await openURL(url)
                        }
                    }
                } else {
                    state.showCancelAlert = true
                }
                return .none

            case .dismissAlert:
                state.showCancelAlert = false
                return .none

            case .continueTapped:
                return .send(.purchase)

            case .compareTapped:
                state.shouldScrollToFeatures.toggle()
                return .none
            }
        }
        .ifLet(\.$destination, action: \.destination)
    }
}

public struct PaywallLoadedViewV2: View {
    @Bindable var store: StoreOf<PaywallLoadedV2>

    public var body: some View {
        ScrollViewReader { proxy in
            StretchingHeaderContent(
                headerHeight: 370,
                header: { offset in
                    auraBackground
                        .overlay {
                            LinearGradient(colors: [.black.opacity(0.5), .black], startPoint: .top, endPoint: .bottom)
                                .blendMode(.destinationOut)
                        }
                        .compositingGroup()
                        .overlay(alignment: .bottom) {
                            VStack {
                                creditBalance

                                if let countdown = store.subscriptionPageContent.countdown {
                                    CountdownView(data: countdown)
                                }

                                Divider()
                                    .padding(.horizontal, 32)
                                    .padding(.vertical, 8)

                                PeriodPickerView(
                                    selectedPeriod: .init(
                                        get: { store.selectedPeriod },
                                        set: { store.send(.periodChanged($0)) }),
                                    data: store.subscriptionPageContent.billingPeriodToggle
                                ) { newPeriod in
                                    store.send(.periodChanged(newPeriod))
                                }
                                .padding()
                            }
                        }
                }, content: {
                    if store.showLegacyWarning {
                        legacyPlanWarning
                            .padding(.horizontal, 24)
                            .frame(maxWidth: .infinity)
                    }
                    
                    if store.showStripeInfo {
                        stripePlanInfo
                            .padding(.horizontal, 24)
                            .frame(maxWidth: .infinity)
                    }

                    planList

                    actions

                    comparisonTableHeader
                        .padding()
                        .id("comparisonTableHeader")

                    comparisonTable

                    footer
                        .padding(.bottom, 36)
                }
            )
            .listRowSpacing(24.0)
            .disablePressDelay()
            .navigationDestination(item: $store.scope(state: \.destination?.topUp, action: \.destination.topUp)) { topUpStore in
                TopUpScreen(store: topUpStore)
                    .customBackButton(background: Material.ultraThin, action: { store.send(.destination(.dismiss)) })
            }
            .alert(
                L10n.FeaturePaywall.cancelPlanTitle,
                isPresented: .init(get: {
                    store.showCancelAlert
                }, set: { _ in
                    store.send(.dismissAlert)
                }), actions: {
                    Button(L10n.FeaturePaywall.cancelSubscription, role: .destructive) {
                        store.send(.openAppStoreSubscriptions)
                    }
                    Button(L10n.FeaturePaywall.cancelPlanDismiss, role: .cancel) {
                        store.send(.dismissAlert)
                    }
                }, message: {
                    Text(L10n.FeaturePaywall.cancelPlanDescription)
                })
            .onAppear {
                store.send(.viewDidAppear)
            }
            .scrollIndicators(.hidden)
            .onChange(of: store.shouldScrollToFeatures) {
                withAnimation(.snappy) {
                    proxy.scrollTo("comparisonTableHeader", anchor: .top)
                }
            }
        }
    }

    @ViewBuilder
    private var creditBalance: some View {
        VStack(spacing: -4) {
            Text(L10n.FeaturePaywall.creditsCount(store.billingInfo.totalCreditsLeft.formatted(.number.notation(.compactName))))
                .typographyV1(.totalCreditsLeft)
                .minimumScaleFactor(0.5)

            if store.isSubscribed {
                // Paid user
                Text(L10n.FeaturePaywall.songsLeftThisMonth(store.billingInfo.songsLeft))
                    .typographyV1(.body1)
            } else {
                // Free user
                Text(L10n.FeatureSettings.songsLeftToday(store.billingInfo.songsLeft))
                    .typographyV1(.body1)
            }
        }
        .multilineTextAlignment(.center)
        .foregroundColor(Color.SemanticV1.textPrimary)
        .frame(alignment: .center)
        .padding(.bottom, 8)
        .padding(.horizontal, 32)
    }

    @ViewBuilder
    var auraBackground: some View {
        #if targetEnvironment(simulator)
            Color.black
        #else
            AuraShaderView(
                id: "paywall",
                appPreset: .pinkYellowOrange,
                morphSpeed: 0.05,
                scale: 1.5,
                seed: 77.0
            )
            .ignoresSafeArea()
        #endif
    }

    private var legacyPlanWarning: some View {
        Button(action: {
            store.send(.openAppStoreSubscriptions)
        }) {
            HStack {
                Image.Icon.warning
                    .foregroundStyle(Color.SemanticV2.accentYellow)
                Text(L10n.FeaturePaywall.legacyPlanWarning)
                    .typographyV1(.body1)
                    .foregroundStyle(Color.SemanticV2.foregroundSecondary)
                    .blendMode(.luminosity)
            }
            .padding()
            .background {
                Color.SemanticV2.accentYellow.opacity(0.15)
            }
            .glassBackground(shape: .rect(cornerRadius: 16.0), type: .regular, fallbackStyle: .ultraThinMaterial, interactive: false)
        }
        .buttonStyle(ScaleButtonStyle(scaleAmount: 0.98))
    }
    
    private var stripePlanInfo: some View {
        Button(action: {
            store.send(.cancelTapped)
        }) {
            HStack {
                Image.Icon.information
                    .foregroundStyle(Color.SemanticV2.iconPrimary)
                Text(L10n.FeaturePaywall.stripePlanInfo)
                    .typographyV1(.body1)
                    .foregroundStyle(Color.SemanticV2.foregroundSecondary)
                    .blendMode(.luminosity)
            }
            .padding()
            .background {
                Color.SemanticV2.backgroundFogThin.opacity(0.15)
            }
            .glassBackground(shape: .rect(cornerRadius: 16.0), type: .regular, fallbackStyle: .ultraThinMaterial, interactive: false)
        }
        .buttonStyle(ScaleButtonStyle(scaleAmount: 0.98))
    }

    @ViewBuilder
    private var planList: some View {
        VStack(spacing: 16) {
            // Filtered paid subscriptions
            let filtered = store.subscriptions
                .filter { $0.period == store.selectedPeriod }
                .sorted(by: { $0.semanticPlan.rawValue < $1.semanticPlan.rawValue })

            subscriptionCard(nil, store.billingInfo, store.purchases, store.isV5Launch)

            ForEach(filtered) { subscription in
                subscriptionCard(subscription, store.billingInfo, store.purchases, store.isV5Launch)
            }
        }
        .frame(maxWidth: .infinity)
        .padding(.horizontal, 16)
    }

    @ViewBuilder
    private var actions: some View {
        VStack(spacing: 12) {
            let disabled = store.selectedSubscription == nil || (store.showStripeInfo && store.selectedSubscription == store.currentSubscription)
            Button {
                store.send(.continueTapped)
            } label: {
                let ctaText: String = {
                    if let selectedSubscription = store.selectedSubscription,
                       let plan = store.subscriptionPageContent.plan(from: selectedSubscription.semanticPlan) {
                        return plan.ctaPrimary?.text ?? L10n.FeaturePaywall.selectPlan
                    }
                    return L10n.FeaturePaywall.selectPlan
                }()
                Text(ctaText)
            }
            .buttonStyle(PillButtonStyleV1(
                isEnabled: !disabled,
                size: .mediumRounded,
                colorCombination: .paywallContinueButton
            ))
            .disabled(disabled)

            HStack(spacing: 12) {
                if store.isSubscribed {
                    Button {
                        store.send(.cancelTapped)
                    } label: {
                        Text(L10n.FeaturePaywall.cancelPlan)
                    }
                    .buttonStyle(PillButtonStyleV1(
                        size: .mediumRounded,
                        colorCombination: .paywallClearButton
                    ))
                }

                Button {
                    store.send(.compareTapped)
                } label: {
                    HStack {
                        Text(store.isSubscribed ? L10n.FeaturePaywall.compare : L10n.FeaturePaywall.compareAllPlanFeatures)
                        Image.Icon.arrowDown
                    }
                }
                .buttonStyle(PillButtonStyleV1(
                    size: .mediumRounded,
                    colorCombination: .paywallClearButton
                ))
            }
        }
        .padding(.horizontal, 16)
    }

    @ViewBuilder
    private var comparisonTableHeader: some View {
        VStack(spacing: 32) {
            Divider()

            Text(L10n.FeaturePaywall.compareAllPlanFeatures)
                .typographyV1(.heading3)
        }
    }

    @ViewBuilder
    private var comparisonTable: some View {
        ComparisonTableView(titles: store.columnTitles, items: store.items)
    }

    @ViewBuilder
    private var footer: some View {
        let baseUrl = BackendEnvironmentProvider.currentConfiguration().webEndpointHost
        let terms = L10n.FeaturePaywall.termsOfUse(baseUrl)
        let privacy = L10n.FeaturePaywall.privacyPolicy(baseUrl)

        HStack(spacing: 16) {
            Button(action: {
                store.send(.delegate(.restore))
            }) {
                Text(L10n.FeaturePaywall.restorePurchases)
            }

            Text(LocalizedStringKey(terms))
            Text(LocalizedStringKey(privacy))
        }
        .typographyV1(.body3)
        .multilineTextAlignment(.center)
        .foregroundStyle(Color.SemanticV1.textSecondary)
        .tint(Color.SemanticV1.textSecondary)
        .frame(maxWidth: .infinity)
    }
    
    var latestPurchase: Entitlement? {
        store.purchases.first
    }

    @ViewBuilder
    private func subscriptionCard(
        _ subscription: Subscription?,
        _ billingInfo: SubscriptionInfoResponse,
        _ purchases: [Entitlement],
        _ isV5Launch: Bool
    ) -> some View {
        let isSubscribed: Bool = {
            if billingInfo.subscriptionPlatform == "stripe" {
                guard let plan = billingInfo.plan, let subscription else { return false }
                return subscription.semanticPlan == .pro &&
                    ((plan.level == 10 && subscription.period == .month) ||
                     (plan.level == 30 && subscription.period == .month))
            }

            guard let subscription else {
                return purchases.isEmpty
            }

            if let latestPurchase {
                return latestPurchase.period == subscription.period &&
                        latestPurchase.plan == subscription.semanticPlan &&
                       !latestPurchase.unsubscribed
            } else {
                return false
            }
        }()

        let plan = store.subscriptionPageContent.plan(from: subscription?.semanticPlan)

        let isDisabled = subscription == nil
        let isDowngradeFromCurrentPlan = subscription?.isDowngrade(from: store.currentSubscription) ?? false

        if let plan {
            SubscriptionCardViewV2(
                subscription: subscription,
                isSubscribed: isSubscribed,
                plan: plan,
                isSelected: store.selectedSubscription == subscription,
                upsellAvailable: !isDowngradeFromCurrentPlan,
                isDisabled: isDisabled,
                currencyCode: store.appStoreCurrencyCode
            ) {
                if !isDisabled {
                    store.send(.tapped(subscription))
                }
            }
        }
    }
}

extension Subscription {
    func isDowngrade(from subscription: Subscription?) -> Bool {
        guard let subscription else { return false }

        // Year → Month is a downgrade
        if subscription.period == .year && self.period == .month {
            return true
        }

        // Premier → Pro/Basic is a downgrade
        if subscription.semanticPlan == .premier,
           self.semanticPlan == .pro || self.semanticPlan == .basic {
            return true
        }

        return false
    }
}

extension SubscriptionPageContentResponse {
    func plan(from semanticPlan: Subscription.SemanticPlan?) -> PlanMarketingResponse? {
        switch semanticPlan {
        case .pro:
            return plans["pro"]
        case .premier:
            return plans["premier"]
        default:
            return plans["free"]
        }
    }
}

extension TypographyV1 {
    static let subscriptionCardTitle: TypographyV1 = .init(
        name: "Subscription Card Title",
        size: 18,
        style: .title,
        weight: .ppNeueMontrealMedium,
    )

    static let subscriptionCardSubtitle: TypographyV1 = .init(
        name: "Subscription Card Subtitle",
        size: 12,
        style: .body,
        weight: .neueMontrealRegular,
    )

    static let totalCreditsLeft: TypographyV1 = .init(
        name: "Total Credits Left",
        size: 60,
        style: .title,
        weight: .editorialNewLight,
        lineHeight: 60
    )
}

extension PillButtonStyleV1.ColorCombination {
    /// White background button with glass effect for Continue button
    static let paywallContinueButton = PillButtonStyleV1.ColorCombination(
        enabled: PillButtonStyleV1.ColorSet(
            foreground: .SemanticV1.alwaysBlack1,
            background: .white,
            loading: .SemanticV1.iconPrimary,
            border: .clear
        ),
        pressed: PillButtonStyleV1.ColorSet(
            foreground: .SemanticV1.alwaysBlack1,
            background: .white.opacity(0.9),
            loading: .SemanticV1.iconPrimary,
            border: .clear
        ),
        disabled: PillButtonStyleV1.ColorSet(
            foreground: .SemanticV1.alwaysBlack1.opacity(0.5),
            background: .white.opacity(0.5),
            loading: .SemanticV1.iconTertiary,
            border: .clear
        )
    )

    /// Clear background button with glass effect for Cancel Plan and Compare buttons
    static let paywallClearButton = PillButtonStyleV1.ColorCombination(
        enabled: PillButtonStyleV1.ColorSet(
            foreground: .SemanticV1.textTertiary,
            background: .clear,
            loading: .SemanticV1.iconPrimary,
            border: .SemanticV1.textTertiary.opacity(0.7),
        ),
        pressed: PillButtonStyleV1.ColorSet(
            foreground: .SemanticV1.textTertiary.opacity(0.7),
            background: .clear,
            loading: .SemanticV1.iconTertiary.opacity(0.7),
            border: .SemanticV1.textTertiary.opacity(0.6),
        ),
        disabled: PillButtonStyleV1.ColorSet(
            foreground: .SemanticV1.textTertiary.opacity(0.4),
            background: .clear,
            loading: .SemanticV1.iconTertiary.opacity(0.4),
            border: .SemanticV1.textTertiary.opacity(0.3),
        )
    )
}
