import APIClient
import ClerkClient
import ComponentLibrary
import ComposableArchitecture
import Localization
import SwiftUI

@Reducer
public struct PhoneAddNumberInPlatform {
    @Reducer(state: .equatable)
    public enum Path {
        case phoneCodeEntry(PhoneCodeEntry)
    }

    @ObservableState
    public struct State: Equatable {
        @Presents public var alert: AlertState<Action.Alert>?
        var phoneAddNumber: PhoneAddNumber.State
        public var path: StackState<Path.State> = .init()

        public init(me: Me) {
            self.phoneAddNumber = PhoneAddNumber.State(me: me, showSkip: false)
        }
    }

    public enum Action {
        public enum Internal {
            case addPhoneNumber(String)
            case addPhoneNumberResponse(Result<String, Error>)
            case closeButtonTapped
        }

        @CasePathable
        public enum Alert: Equatable {
            case dismiss
        }

        public enum Delegate {
            case validationSuccess(String)
        }

        case dismiss
        case phoneAddNumber(PhoneAddNumber.Action)
        case path(StackAction<Path.State, Path.Action>)
        case `internal`(Internal)
        case alert(PresentationAction<Alert>)
        case delegate(Delegate)
    }

    public init() {}

    @Dependency(\.dismiss) var dismiss
    @Dependency(APIClient.self) var apiClient

    public var body: some ReducerOf<Self> {
        Scope(state: \.phoneAddNumber, action: \.phoneAddNumber) {
            PhoneAddNumber()
        }
        Reduce { state, action in
            switch action {
            case .dismiss:
                return .run { _ in await self.dismiss() }

            case .phoneAddNumber(.delegate(.alreadyValidated(_, let phoneNumber))):
                return .send(.internal(.addPhoneNumber(phoneNumber)))

            case .phoneAddNumber(.delegate(.phoneCodeSent(let phoneNumberEnvelope, _))):
                state.path.append(.phoneCodeEntry(PhoneCodeEntry.State(phone: phoneNumberEnvelope.e164Formatted, path: .addNumberInPlatform(phoneNumberEnvelope))))
                return .none

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

            case .path(.element(_, .phoneCodeEntry(.delegate(.validationSucceeded(.addNumberInPlatform(let phone), authenticationInfo: _))))):
                return .run { send in
                    await send(.internal(.addPhoneNumberResponse(Result(catching: {
                        try await apiClient.updatePhoneNumber(phoneNumber: phone.e164Formatted)
                        return phone.e164Formatted
                    }))))
                }

            case .internal(.addPhoneNumberResponse(let result)):
                switch result {
                case .success(let phoneNumber):
                    return .merge(
                        .send(.delegate(.validationSuccess(phoneNumber))),
                        .send(.dismiss)
                    )

                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 .internal(.closeButtonTapped):
                return .send(.dismiss)

            case .phoneAddNumber:
                return .none

            case .path:
                return .none

            case .internal:
                return .none

            case .alert:
                return .none

            case .delegate:
                return .none
            }
        }
        .forEach(\.path, action: \.path)
        .ifLet(\.$alert, action: \.alert)

        Analytics()
    }
}

public struct PhoneAddNumberInPlatformView: View {
    @Bindable var store: StoreOf<PhoneAddNumberInPlatform>

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

    public var body: some View {
        NavigationStack(
            path: $store.scope(state: \.path, action: \.path),
            root: {
                PhoneAddNumberScreen(store: store.scope(state: \.phoneAddNumber, action: \.phoneAddNumber))
                    .toolbar {
                        ToolbarItem(placement: .topBarTrailing) {
                            ToolbarButton(.close, background: Color.SemanticV1.backgroundQuaternary) {
                                store.send(.internal(.closeButtonTapped))
                            }
                        }
                    }
            },
            destination: { path in
                switch path.case {
                case .phoneCodeEntry(let store):
                    PhoneCodeEntryScreen(store: store)
                }
            }
        )
        .alert($store.scope(state: \.alert, action: \.alert))
    }
}
