import APIClient
import BackendEnvironmentClient
import ClerkClient
import ComponentLibrary
import ComposableArchitecture
import EventBusClient
import FeatureBrandedAlert
import FeaturePaywall
import FeatureSocial
import Localization
import NavigationRouterClient
import StatsigClient
import SunoModelClient
import SwiftUI
import UserEventBusClient
import Utilities

// swiftlint:disable file_length
// swiftlint:disable multiple_closures_with_trailing_closure

@Reducer
public struct SettingsV2 {
    public enum SettingsV2Error: LocalizedError {
        case openSupportURLError(String)

        public var errorDescription: String? {
            switch self {
            case .openSupportURLError(let urlString): "Failed to create support URL from String: \(urlString)"
            }
        }
    }

    @Reducer(state: .equatable)
    public enum Destination {
        case account(Account)
        case subscriptions(PaywallV1)
        // FIXME: Remove after Nav V2 ^
        case alert(AlertState<Alert>)

        public enum Alert {
            case logout
            case cancelLogout
            case navigateToSettings
        }
    }

    @ObservableState
    public struct State: Equatable {
        @Presents public var destination: Destination.State?

        @Shared(.inMemory(.promoCodeUrl)) var promoCodeUrl: String?
        @Shared(.inMemory(.promoCodeUrlRedemptionsLeft)) var promoCodeRedemptionsLeft: Int?
        @Shared(.inMemory(.selectedSunoModel)) var selectedSunoModel: SunoModelMetaData = .modelDefault
        @Shared(.inMemory(.sunoModelUserAccessCategory)) var sunoModelUserAccessCategory: SunoModelUserAccess = .defaultLimitedAccess

        var isSigningOut = false
        @Shared var me: Me
        @Shared(.inMemory(.billingInfo)) var billingInfo: SubscriptionInfoResponse?
        var isLoadingBilling = false

        @Shared(.appStorage(.hapticsEnabled)) var hapticsEnabled: Bool = true

        // "English", "Spanish", etc.
        var currentLanguage: String = Bundle.preferredLanguage()

        var subscriptionTitle: String {
            if billingInfo?.plan == nil {
                return L10n.FeatureSettings.upgradeNow
            } else {
                return L10n.FeatureSettings.subscribed
            }
        }

        var songsLeftText: String {
            if !isLoadingBilling, billingInfo?.plan?.level == 0 || billingInfo?.plan == nil {
                let creditsLeft = billingInfo?.totalCreditsLeft ?? 0
                let creditsPerSong = 5
                return L10n.FeatureSettings.songsLeftToday(Int(creditsLeft / creditsPerSong))
            }
            return ""
        }

        public var supportUrlString: String {
            let supportUrl = Bundle.main.infoDictionary?["SUPPORT_ENDPOINT"] as? String ?? "help.suno.com"
            return "https://\(supportUrl)"
        }

        public var feedbackUrlString: String {
            let feedbackUrl = Bundle.main.infoDictionary?["FEEDBACK_ENDPOINT"] as? String ?? "sunomusic.typeform.com/to/jqaDLbPK"
            let baseUrl = "https://\(feedbackUrl)"

            var params: [String: String] = [:]

            if let appVersion = Bundle.main.appVersion,
               let appBuildNumber = Bundle.main.appBuildNumber {
                params["app_version"] = appVersion
                params["app_build"] = appBuildNumber
            }

            let handle = me.user.handle
            if !handle.isEmpty {
                params["handle"] = handle
            }

            let identifier = me.user.email.isEmpty ? me.user.phoneNumber : me.user.email
            if let identifier = identifier, !identifier.isEmpty {
                params["identifier"] = identifier
            }

            guard !params.isEmpty else { return baseUrl }

            let fragment = params
                .map { "\($0.key)=\($0.value.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) ?? $0.value)" }
                .joined(separator: "&")

            return "\(baseUrl)#\(fragment)"
        }

        public init(me: Shared<Me>) {
            self._me = me
        }
    }

    public enum Action {
        case destination(PresentationAction<Destination.Action>)
        case loadBilling
        case accountTapped
        case subscriptionsTapped
        case supportTapped
        case reportProblemTapped
        case logoutTapped
        case languageTapped
        case logoutConfirmed
        case billingInfoResponse(Result<SubscriptionInfoResponse, Error>)
        case showBrandedAlert(BrandedAlertStyle)
        case delegate(Delegate)
        case task
        case appearanceTapped
        case hapticsToggled(Bool)

        public enum Delegate {
            case playClipsAt(Clip, [Clip])
            case goToCreate
        }
    }

    @Dependency(ClerkClient.self) var clerk
    @Dependency(APIClient.self) var apiClient
    @Dependency(\.telemetryClient) var telemetry
    @Dependency(SunoModelClient.self) var sunoModelClient
    @Dependency(NavigationRouterClient.self) var navigationRouter
    @Dependency(\.userEventBus.send) var sendUserEvent

    public init() {}

    public var body: some ReducerOf<Self> {
        Reduce { state, action in
            struct UserChannelCancellableId: Hashable {}
            switch action {
            case .task:
                return .send(.loadBilling)

            case .loadBilling:
                state.isLoadingBilling = true
                return .run { send in
                    await send(.billingInfoResponse(Result(catching: { try await APIClientV2.underlying.send(Paths.billing.info.get).value })))
                }

            case .billingInfoResponse(.success(let billingInfo)):
                state.isLoadingBilling = false
                state.$billingInfo.withLock { $0 = billingInfo }
                return .none

            case .billingInfoResponse(.failure(let error)):
                state.isLoadingBilling = false
                log.telemetry.error(error)
                return .none

            case .accountTapped:
                navigationRouter.send(route: .account)
                return .none

            case .subscriptionsTapped:
                navigationRouter.send(route: .subscriptions)
                return .none

            case .supportTapped:
                guard let url = URL(string: state.supportUrlString) else {
                    log.telemetry.error(SettingsV2Error.openSupportURLError(state.supportUrlString))
                    return .none
                }
                navigationRouter.send(route: .webView(title: L10n.FeatureSettings.support, url: url))
                return .none

            case .reportProblemTapped:
                guard let url = URL(string: state.feedbackUrlString) else {
                    log.telemetry.error(SettingsV2Error.openSupportURLError(state.feedbackUrlString))
                    return .none
                }
                navigationRouter.send(route: .webView(title: L10n.FeatureSettings.reportProblem, url: url))
                return .none

            case .logoutTapped:
                state.isSigningOut = true
                state.destination = .alert(
                    AlertState {
                        TextState(L10n.FeatureSettings.logOutTitle)
                    } actions: {
                        ButtonState(role: .destructive, action: .send(.logout)) {
                            TextState(L10n.FeatureSettings.logOutButton)
                        }
                        ButtonState(role: .cancel, action: .send(.cancelLogout)) {
                            TextState(L10n.FeatureSettings.cancel)
                        }
                    } message: {
                        TextState(L10n.FeatureSettings.logOutMessage)
                    }
                )
                return .none

            case .logoutConfirmed:
                sendUserEvent(.signOut)
                // Nav V1: Handled in AppCoordinator
                return .none

            case .destination(.presented(.alert(.logout))):
                return .send(.logoutConfirmed)

            case .destination(.presented(.alert(.cancelLogout))):
                state.isSigningOut = false
                return .none

            case .destination(.presented(.alert(.navigateToSettings))):
                guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else { return .none }
                return .run { @MainActor _ in
                    await UIApplication.shared.open(settingsUrl)
                }

            case .languageTapped:
                // Show the alert saying "You'll be taken to settings to change your language" first
                state.destination = .alert(
                    AlertState {
                        TextState(L10n.FeatureSettings.languageAlertTitle)
                    } actions: {
                        ButtonState(action: .send(.navigateToSettings)) {
                            TextState(L10n.FeatureSettings.languageAlertButton)
                        }
                        ButtonState(role: .cancel, action: .send(.none)) {
                            TextState(L10n.FeatureSettings.cancel)
                        }
                    } message: {
                        TextState(L10n.FeatureSettings.languageAlertMessage)
                    }
                )
                return .none

            case .appearanceTapped:
                navigationRouter.send(route: .appearance)
                return .none

            case .hapticsToggled(let enabled):
                state.$hapticsEnabled.withLock { $0 = enabled }
                return .none

            case .destination,
                 .delegate,
                 .showBrandedAlert:
                // Catch-all
                return .none
            }
        }
        .ifLet(\.$destination, action: \.destination)

        Analytics()
    }
}

public struct SettingsV2Screen: View {
    @Bindable var store: StoreOf<SettingsV2>
    @Namespace var namespace

    public init(store: StoreOf<SettingsV2>) {
        self.store = store
    }

    public var body: some View {
        settingsRows
            .padding(8)
            .padding(.bottom, 60)
            .background(Color.SemanticV1.backgroundPrimary)
            .navigationBarTitleDisplayMode(.inline)
            .navigationTitle(L10n.FeatureSettings.v2Title)
            .alert($store.scope(state: \.destination?.alert, action: \.destination.alert))
            .task {
                store.send(.task)
            }
    }

    @ViewBuilder
    private var settingsRows: some View {
        VStack(spacing: .zero) {
            SettingsRow(icon: Image.Icon.account, title: L10n.FeatureSettings.account) {
                store.send(.accountTapped)
            }

            if store.me.flags[FlagKey.iosSubscriptions] == true {
                SettingsRow(icon: Image.Icon.genres, title: L10n.FeatureSettings.mySubscription, trailingView: { subscriptionTrailingView }) {
                    store.send(.subscriptionsTapped)
                }
            }

            SettingsRow(icon: Image.Icon.globe, title: L10n.FeatureSettings.language, trailingView: { currentLanguageTrailingView }) {
                store.send(.languageTapped)
            }

            if FeatureFlag.legacy.showAppearanceSetting {
                SettingsRow(icon: Image.Icon.appearance, title: L10n.FeatureSettings.appearance) {
                    store.send(.appearanceTapped)
                }
            }

            if FeatureFlag.legacy.hapticsClientEnabled {
                SettingsRow(icon: Image.Icon.handHorns, title: L10n.FeatureSettings.hapticFeedback, trailingView: {
                    Toggle(isOn: Binding(
                        get: { store.hapticsEnabled },
                        set: { newValue in
                            store.send(.hapticsToggled(newValue))
                        }
                    )) {
                        EmptyView()
                    }
                    .tint(Color.SemanticV2.accentBrand)
                    .labelsHidden()
                }) {
                    // No action when row is tapped - toggle handles it
                }
            }

            SettingsRow(icon: Image.Icon.flag, title: L10n.FeatureSettings.reportProblem, trailingView: { Image.Icon.arrowUp.foregroundColor(.SemanticV1.iconPrimary) }) {
                store.send(.reportProblemTapped)
            }

            SettingsRow(icon: Image.Icon.phone, title: L10n.FeatureSettings.support, trailingView: { Image.Icon.arrowUp.foregroundColor(.SemanticV1.iconPrimary) }) {
                store.send(.supportTapped)
            }
            SettingsRow(icon: Image.Icon.logout, title: L10n.FeatureSettings.logOutButton, isLoading: store.isSigningOut, style: .destructive) {
                store.send(.logoutTapped)
            }

            Spacer()

            footer
        }
    }

    private var footer: some View {
        VStack(spacing: 16) {
            #if DEBUG
            Text("Debug Build")
                .foregroundStyle(Color.SemanticV1.textSecondary)
                .textCase(.uppercase)
                .typographyV1(.monospace)
            #endif
            
            HStack(alignment: .firstTextBaseline, spacing: 4) {
                Image.Icon.madeWithSunoLogo
                    .resizable()
                    .scaledToFit()
                    .foregroundColor(Color.SemanticV1.iconPrimary)
                    .frame(height: 10)

                let appVersion = Bundle.main.appVersion ?? "1"
                Text("• 2025 • v\(appVersion)")
                    .typographyV1(.monospace)
                    .foregroundStyle(Color.SemanticV1.textPrimary)
            }
            .safePadding()
        }
    }

    @ViewBuilder
    private var subscriptionTrailingView: some View {
        Text(store.subscriptionTitle)
            .typographyV1(.body1)
            .overlay {
                Group {
                    if isSubscribed {
                        Color.SemanticV1.textSecondary
                    } else {
                        LinearGradient(
                            colors: [
                                Color(red: 16 / 255, green: 0 / 255, blue: 192 / 255),
                                Color(red: 242 / 255, green: 64 / 255, blue: 24 / 255),
                            ],
                            startPoint: .leading,
                            endPoint: .trailing
                        )
                    }
                }
                .mask {
                    Text(store.subscriptionTitle)
                        .typographyV1(.body1)
                }
            }
            .opacity(store.isLoadingBilling ? 0 : 1)
            .padding(.horizontal, 11)
            .padding(.vertical, 7)
            .background(RoundedRectangle(cornerRadius: 8).fill(Color.SemanticV1.backgroundTertiary))
            .overlay {
                if store.isLoadingBilling {
                    ProgressView()
                        .progressViewStyle(.circular)
                }
            }
    }

    @ViewBuilder
    private var currentLanguageTrailingView: some View {
        Text(store.currentLanguage)
            .typographyV1(.body1)
            .foregroundStyle(Color.SemanticV1.textPrimary)
            .underline(true, color: Color.SemanticV1.textPrimary)
            .padding(.trailing, 2)
    }

    private var isSubscribed: Bool {
        return store.billingInfo?.plan != nil
    }
}
