import APIClient
import Clerk
import ClerkClient
import ComposableArchitecture
import Localization
import PhoneNumberKit
import SwiftUI

@Reducer
public struct PhoneAddNumber {
    @ObservableState
    public struct State: Equatable {
        @Presents public var alert: AlertState<Action.Alert>?

        public var isWorking = false
        public var isFocused = true

        public var value: String = ""

        public var me: Me
        public var showSkip: Bool

        public init(me: Me, showSkip: Bool = true) {
            self.me = me
            self.showSkip = showSkip
        }
    }

    public enum Action: BindableAction {
        public enum Internal {
            case startPhoneValidationResponse(Result<PhoneNumberValidationStatus, Error>)
            case addPhoneNumber(String)
            case addPhoneNumberResponse(Result<Void, Error>)
        }

        public enum Delegate {
            case phoneCodeSent(PhoneNumberEnvelope, Me)
            case skipTapped(Me)
            case alreadyValidated(Me, String)
        }

        @CasePathable
        public enum Alert: Equatable {
            case dismiss
        }

        case confirmTapped
        case setFocus(Bool)

        case binding(BindingAction<State>)
        case `internal`(Internal)
        case delegate(Delegate)
        case alert(PresentationAction<Alert>)
    }

    public init() {}

    @Dependency(APIClient.self) var apiClient
    @Dependency(ClerkClient.self) var clerk

    public var body: some ReducerOf<Self> {
        BindingReducer()

        Reduce { state, action in
            switch action {
            case .confirmTapped:
                state.isWorking = true
                return .run { [phone = state.value] send in
                    await send(.internal(.startPhoneValidationResponse(Result(catching: { try await clerk.addPhoneNumberToAccount(phone) }))))
                }

            case .setFocus(let value):
                state.isFocused = value
                return .none

            case .internal(.startPhoneValidationResponse(let result)):
                state.isWorking = false

                switch result {
                case .success(let status):
                    switch status {
                    case .oneTimeCodeSent(let clerkPhoneNumber):
                        return .send(.delegate(.phoneCodeSent(clerkPhoneNumber, state.me)))
                    case .validated:
                        return .send(.internal(.addPhoneNumber(state.value)))
                    }

                case .failure(let error):
                    state.alert = .init(
                        title: { TextState(L10n.FeatureOnboarding.errorTitle) },
                        actions: {
                            ButtonState { TextState(L10n.FeatureOnboarding.errorButton) }
                        },
                        message: { TextState(error.underlyingError) }
                    )
                    return .none
                }

            case .internal(.addPhoneNumber(let phone)):
                return .run { send in
                    await send(.internal(.addPhoneNumberResponse(Result(catching: { try await apiClient.updatePhoneNumber(phoneNumber: phone.formatPhoneNumber()) }))))
                }

            case .internal(.addPhoneNumberResponse(let result)):
                switch result {
                case .success:
                    return .send(.delegate(.alreadyValidated(state.me, state.value)))
                case .failure(let error):
                    state.alert = .init(
                        title: { TextState(L10n.FeatureSocial.errorTitle) },
                        actions: {
                            ButtonState { TextState(L10n.FeatureSocial.errorButton) }
                        },
                        message: { TextState(error.underlyingError) }
                    )
                    return .none
                }

            case .alert(.dismiss), .alert(.presented(.dismiss)):
                state.alert = nil
                return .none

            case .binding:
                // catch-all
                return .none

            case .delegate:
                // Handled upstream
                return .none
            }
        }
        .ifLet(\.$alert, action: \.alert)

        Analytics()
    }
}

public struct PhoneAddNumberScreen: View {
    @Bindable var store: StoreOf<PhoneAddNumber>
    @FocusState private var focusedState: Bool

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

    public var body: some View {
        PhoneEntryView(
            title: L10n.FeatureSocial.phoneEntryAddNumberNavigationTitle,
            isWorking: store.isWorking,
            footer: L10n.FeatureSocial.phoneEntryAddNumberFooter,
            confirmTapped: { store.send(.confirmTapped) },
            value: $store.value,
            focused: $store.isFocused.sending(\.setFocus)
        )
        .toolbar {
            if store.showSkip {
                ToolbarItem(placement: .topBarLeading) {
                    Button(L10n.FeatureSocial.skip) {
                        store.send(.delegate(.skipTapped(store.me)))
                    }
                    .typographyV1(.body3)
                    .foregroundStyle(Color.SemanticV1.textLink)
                    .buttonStyle(.plain)
                }
            }
        }
        .alert($store.scope(state: \.alert, action: \.alert))
    }
}
