//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Foundation

extension CredentialStoreState {

    struct Resolver: StateMachineResolver {
        typealias StateType = CredentialStoreState
        let defaultState = CredentialStoreState.notConfigured

        init() { }

        func resolve(
            oldState: StateType,
            byApplying event: StateMachineEvent
        ) -> StateResolution<StateType> {
            guard let credentialStoreEvent = isCredentialStoreEvent(event) else {
                return .from(oldState)
            }
            switch oldState {
            case .notConfigured:
                return resolveNotConfigured(byApplying: credentialStoreEvent)
            case .migratingLegacyStore:
                return resolveMigratingLegacyStore(oldState: oldState, byApplying: credentialStoreEvent)
            case .loadingStoredCredentials, .storingCredentials:
                return resolveOperationCompletion(oldState: oldState, byApplying: credentialStoreEvent)
            case .clearingCredentials:
                return resolveClearingCredentials(oldState: oldState, byApplying: credentialStoreEvent)
            case .success, .error, .clearedCredential:
                return resolveSuccessAndErrorState(oldState: oldState, byApplying: credentialStoreEvent)
            case .idle:
                return resolveIdleState(oldState: oldState, byApplying: credentialStoreEvent)
            }
        }

        private func resolveNotConfigured(
            byApplying credentialStoreEvent: CredentialStoreEvent
        ) -> StateResolution<StateType> {

            switch credentialStoreEvent.eventType {
            case .migrateLegacyCredentialStore, .loadCredentialStore:
                let action = MigrateLegacyCredentialStore()
                let resolution = StateResolution(
                    newState: CredentialStoreState.migratingLegacyStore,
                    actions: [action]
                )
                return resolution
            default:
                return .from(.notConfigured)
            }
        }

        private func resolveMigratingLegacyStore(
            oldState: StateType,
            byApplying credentialStoreEvent: CredentialStoreEvent
        ) -> StateResolution<StateType> {

            switch credentialStoreEvent.eventType {
            case .loadCredentialStore(let type):
                let action = LoadCredentialStore(credentialStoreType: type)
                let resolution = StateResolution(
                    newState: CredentialStoreState.loadingStoredCredentials,
                    actions: [action]
                )
                return resolution
            case .throwError(let error):
                return .init(newState: CredentialStoreState.error(error))
            default:
                return .from(oldState)
            }
        }

        private func resolveOperationCompletion(
            oldState: StateType,
            byApplying credentialStoreEvent: CredentialStoreEvent
        ) -> StateResolution<StateType> {
            switch credentialStoreEvent.eventType {
            case .completedOperation(let storedCredentials):
                let action = IdleCredentialStore()
                let resolution = StateResolution(
                    newState: CredentialStoreState.success(storedCredentials),
                    actions: [action]
                )
                return resolution
            case .throwError(let error):
                let action = IdleCredentialStore()
                let resolution = StateResolution(
                    newState: CredentialStoreState.error(error),
                    actions: [action]
                )
                return resolution
            default:
                return .from(oldState)
            }
        }

        private func resolveClearingCredentials(
            oldState: StateType,
            byApplying credentialStoreEvent: CredentialStoreEvent
        ) -> StateResolution<StateType> {
            switch credentialStoreEvent.eventType {
            case .credentialCleared(let dataType):
                let action = IdleCredentialStore()
                let resolution = StateResolution(
                    newState: CredentialStoreState.clearedCredential(dataType),
                    actions: [action]
                )
                return resolution
            case .throwError(let error):
                let action = IdleCredentialStore()
                let resolution = StateResolution(
                    newState: CredentialStoreState.error(error),
                    actions: [action]
                )
                return resolution
            default:
                return .from(oldState)
            }
        }

        private func resolveSuccessAndErrorState(
            oldState: StateType,
            byApplying credentialStoreEvent: CredentialStoreEvent
        ) -> StateResolution<StateType> {
            switch credentialStoreEvent.eventType {
            case .moveToIdleState:
                return .init(newState: CredentialStoreState.idle)
            default:
                return .from(oldState)
            }
        }

        private func resolveIdleState(
            oldState: StateType,
            byApplying credentialStoreEvent: CredentialStoreEvent
        ) -> StateResolution<StateType> {
            switch credentialStoreEvent.eventType {
            case .loadCredentialStore(let type):
                let action = LoadCredentialStore(credentialStoreType: type)
                let resolution = StateResolution(
                    newState: CredentialStoreState.loadingStoredCredentials,
                    actions: [action]
                )
                return resolution
            case .storeCredentials(let credentials):
                let action = StoreCredentials(credentials: credentials)
                let resolution = StateResolution(
                    newState: CredentialStoreState.storingCredentials,
                    actions: [action]
                )
                return resolution
            case .clearCredentialStore(let type):
                let action = ClearCredentialStore(dataStoreType: type)
                let resolution = StateResolution(
                    newState: CredentialStoreState.clearingCredentials,
                    actions: [action]
                )
                return resolution
            default:
                return .from(oldState)
            }
        }

        private func isCredentialStoreEvent(_ event: StateMachineEvent) -> CredentialStoreEvent? {
            guard let credentialStore = event as? CredentialStoreEvent else {
                return nil
            }
            return credentialStore
        }

    }
}