import 'package:core/data/network/config/dynamic_url_interceptors.dart';
import 'package:core/presentation/extensions/url_extension.dart';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/build_utils.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/account/password.dart';
import 'package:model/account/user_name.dart';
import 'package:model/oidc/oidc_configuration.dart';
import 'package:model/oidc/request/oidc_request.dart';
import 'package:model/oidc/response/oidc_response.dart';
import 'package:model/oidc/token_oidc.dart';
import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart';
import 'package:tmail_ui_user/features/login/domain/model/recent_login_url.dart';
import 'package:tmail_ui_user/features/login/domain/model/recent_login_username.dart';
import 'package:tmail_ui_user/features/login/domain/state/authenticate_oidc_on_browser_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/authentication_user_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/check_oidc_is_available_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_all_recent_login_url_latest_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_all_recent_login_username_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_authentication_info_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_oidc_configuration_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_oidc_is_available_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_stored_oidc_configuration_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_token_oidc_state.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/authenticate_oidc_on_browser_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/check_oidc_is_available_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_url_on_mobile_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_username_on_mobile_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_authentication_info_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_oidc_configuration_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_oidc_is_available_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_stored_oidc_configuration_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_token_oidc_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/save_login_url_on_mobile_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/save_login_username_on_mobile_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/update_authentication_account_interactor.dart';
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
import 'package:tmail_ui_user/features/login/presentation/model/login_arguments.dart';
import 'package:tmail_ui_user/features/login/presentation/state/login_state.dart';
import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart';
import 'package:tmail_ui_user/features/network_status_handle/presentation/network_connnection_controller.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
import 'package:tmail_ui_user/main/routes/navigation_router.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
import 'package:tmail_ui_user/main/routes/route_utils.dart';
import 'package:tmail_ui_user/main/utils/app_config.dart';

class LoginController extends ReloadableController {

  final AuthenticationInteractor _authenticationInteractor;
  final DynamicUrlInterceptors _dynamicUrlInterceptors;
  final CheckOIDCIsAvailableInteractor _checkOIDCIsAvailableInteractor;
  final GetOIDCIsAvailableInteractor _getOIDCIsAvailableInteractor;
  final GetOIDCConfigurationInteractor _getOIDCConfigurationInteractor;
  final GetTokenOIDCInteractor _getTokenOIDCInteractor;
  final AuthenticateOidcOnBrowserInteractor _authenticateOidcOnBrowserInteractor;
  final GetAuthenticationInfoInteractor _getAuthenticationInfoInteractor;
  final GetStoredOidcConfigurationInteractor _getStoredOidcConfigurationInteractor;
  final SaveLoginUrlOnMobileInteractor _saveLoginUrlOnMobileInteractor;
  final GetAllRecentLoginUrlOnMobileInteractor _getAllRecentLoginUrlOnMobileInteractor;
  final SaveLoginUsernameOnMobileInteractor _saveLoginUsernameOnMobileInteractor;
  final GetAllRecentLoginUsernameOnMobileInteractor _getAllRecentLoginUsernameOnMobileInteractor;


  final TextEditingController urlInputController = TextEditingController();
  final TextEditingController usernameInputController = TextEditingController();
  final NetworkConnectionController networkConnectionController = Get.find<NetworkConnectionController>();

  LoginController(
    LogoutOidcInteractor logoutOidcInteractor,
    DeleteAuthorityOidcInteractor deleteAuthorityOidcInteractor,
    GetAuthenticatedAccountInteractor getAuthenticatedAccountInteractor,
    UpdateAuthenticationAccountInteractor updateAuthenticationAccountInteractor,
    this._authenticationInteractor,
    this._dynamicUrlInterceptors,
    this._checkOIDCIsAvailableInteractor,
    this._getOIDCIsAvailableInteractor,
    this._getOIDCConfigurationInteractor,
    this._getTokenOIDCInteractor,
    this._authenticateOidcOnBrowserInteractor,
    this._getAuthenticationInfoInteractor,
    this._getStoredOidcConfigurationInteractor,
    this._saveLoginUrlOnMobileInteractor,
    this._getAllRecentLoginUrlOnMobileInteractor,
    this._saveLoginUsernameOnMobileInteractor,
    this._getAllRecentLoginUsernameOnMobileInteractor,
  ) : super(
    getAuthenticatedAccountInteractor,
    updateAuthenticationAccountInteractor
  );

  var loginState = LoginState(Right(LoginInitAction())).obs;
  final loginFormType = LoginFormType.baseUrlForm.obs;

  String? _urlText;
  String? _userNameText;
  String? _passwordText;
  OIDCResponse? _oidcResponse;

  void setUrlText(String url) => _urlText = url.trim().formatURLValid();

  void setUserNameText(String userName) => _userNameText = userName;

  void setPasswordText(String password) => _passwordText = password;

  Uri? _parseUri(String? url) => url != null && url.trim().isNotEmpty
      ? Uri.parse(url.trim())
      : null;

  UserName? _parseUserName(String? userName) => userName != null && userName.trim().isNotEmpty
      ? UserName(userName.trim())
      : null;

  Password? _parsePassword(String? password) => password != null && password.trim().isNotEmpty
      ? Password(password.trim())
      : null;

  @override
  void onReady() {
    super.onReady();
    if (BuildUtils.isWeb) {
      final arguments = Get.arguments;
      if (arguments is LoginArguments) {
        loginFormType.value = arguments.loginFormType;
        _checkOIDCIsAvailable();
      } else {
        _getAuthenticationInfo();
      }
    }
  }

  @override
  void onData(Either<Failure, Success> newState) {
    super.onData(newState);
    viewState.value.fold(
      (failure) {
        if (failure is GetAuthenticationInfoFailure) {
          getAuthenticatedAccountAction();
        } else if (failure is CheckOIDCIsAvailableFailure ||
            failure is GetStoredOidcConfigurationFailure) {
          _showFormLoginWithCredentialAction();
        } else if (failure is GetOIDCIsAvailableFailure) {
          loginState.value = LoginState(Left(LoginSSONotAvailableAction()));
          _showFormLoginWithCredentialAction();
        } else if (failure is AuthenticationUserFailure) {
          _loginFailureAction(failure);
        } else if (failure is GetOIDCConfigurationFailure ||
            failure is GetTokenOIDCFailure ||
            failure is AuthenticateOidcOnBrowserFailure) {
          loginState.value = LoginState(Left(failure));
        }
      },
      (success) {
        if (success is GetAuthenticationInfoSuccess) {
          _getStoredOidcConfiguration();
        } else if (success is GetStoredOidcConfigurationSuccess) {
          _getTokenOIDCAction(success.oidcConfiguration);
        } else if (success is CheckOIDCIsAvailableSuccess) {
          _showFormLoginWithSSOAction(success);
        } else if (success is GetOIDCIsAvailableSuccess) {
          loginState.value = LoginState(Right(success));
          _oidcResponse = success.oidcResponse;
          _getOIDCConfiguration();
        } else if (success is GetOIDCConfigurationSuccess) {
          _getOIDCConfigurationSuccess(success);
        } else if (success is GetTokenOIDCSuccess) {
          _getTokenOIDCSuccess(success);
        } else if (success is AuthenticationUserSuccess) {
          _loginSuccessAction(success);
        } else if (success is GetAuthenticationInfoLoading ||
            success is CheckOIDCIsAvailableLoading ||
            success is GetStoredOidcConfigurationLoading ||
            success is GetOIDCConfigurationLoading ||
            success is GetTokenOIDCLoading ||
            success is AuthenticationUserLoading ||
            success is GetOIDCIsAvailableLoading) {
          loginState.value = LoginState(Right(LoginLoadingAction()));
        }
      }
    );
  }

  @override
  void handleReloaded(Session session) {
    pushAndPop(
      RouteUtils.generateNavigationRoute(AppRoutes.dashboard, NavigationRouter()),
      arguments: session);
  }

  void _getAuthenticationInfo() {
    consumeState(_getAuthenticationInfoInteractor.execute());
  }

  void _getStoredOidcConfiguration() {
    consumeState(_getStoredOidcConfigurationInteractor.execute());
  }

  void handleNextInUrlInputFormPress() {
    _saveRecentLoginUrl();
    _checkOIDCIsAvailable();
  }

  void _checkOIDCIsAvailable() {
    final baseUri = BuildUtils.isWeb ? _parseUri(AppConfig.baseUrl) : _parseUri(_urlText);
    if (baseUri == null) {
      loginState.value = LoginState(Left(LoginMissUrlAction()));
    } else {
      consumeState(_checkOIDCIsAvailableInteractor.execute(OIDCRequest(
          baseUrl: baseUri.toString(),
          resourceUrl: baseUri.origin)));
    }
  }

  void _showFormLoginWithSSOAction(CheckOIDCIsAvailableSuccess success) {
    loginState.value = LoginState(Right(success));
    loginFormType.value = LoginFormType.ssoForm;
    _oidcResponse = success.oidcResponse;
  }

  void handleBackInCredentialForm() {
    loginState.value = LoginState(Right(LoginInitAction()));
    loginFormType.value = LoginFormType.baseUrlForm;
  }

  void _showFormLoginWithCredentialAction() {
    loginState.value = LoginState(Right(InputUrlCompletion()));
    loginFormType.value = LoginFormType.credentialForm;
  }

  void handleLoginPressed() {
    _saveRecentLoginUsername();
    log('LoginController::handleLoginPressed(): ${loginFormType.value}');
    if (loginFormType.value == LoginFormType.ssoForm) {
      _getOIDCConfiguration();
    } else {
      final baseUri = kIsWeb ? _parseUri(AppConfig.baseUrl) : _parseUri(_urlText);
      final userName = _parseUserName(_userNameText);
      final password = _parsePassword(_passwordText);
      if (baseUri != null && userName != null && password != null) {
        _loginAction(baseUri, userName, password);
      } else if (baseUri == null) {
        loginState.value = LoginState(Left(LoginMissUrlAction()));
      } else if (userName == null) {
        loginState.value = LoginState(Left(LoginMissUsernameAction()));
      } else if (password == null) {
        loginState.value = LoginState(Left(LoginMissPasswordAction()));
      }
    }
  }

  void handleSSOPressed() {
    final baseUri = _parseUri(AppConfig.baseUrl);
    if (baseUri != null) {
      consumeState(_getOIDCIsAvailableInteractor.execute(OIDCRequest(
          baseUrl: baseUri.toString(),
          resourceUrl: baseUri.origin)));
    }  else {
      loginState.value = LoginState(Left(LoginCanNotAuthenticationSSOAction()));
    }
  }

  void _getOIDCConfiguration() {
    if (_oidcResponse != null) {
      consumeState(_getOIDCConfigurationInteractor.execute(_oidcResponse!));
    } else {
      loginState.value = LoginState(Left(LoginCanNotAuthenticationSSOAction()));
    }
  }

  void _getOIDCConfigurationSuccess(GetOIDCConfigurationSuccess success) {
    loginState.value = LoginState(Right(success));
    if (BuildUtils.isWeb) {
      _authenticateOidcOnBrowserAction(success.oidcConfiguration);
    } else {
      _getTokenOIDCAction(success.oidcConfiguration);
    }
  }

  void _getTokenOIDCAction(OIDCConfiguration config) async {
    final baseUri = kIsWeb ? _parseUri(AppConfig.baseUrl) : _parseUri(_urlText);
    if (baseUri != null) {
     consumeState(_getTokenOIDCInteractor.execute(baseUri, config));
    } else {
      loginState.value = LoginState(Left(LoginCanNotGetTokenAction()));
    }
  }

  void _authenticateOidcOnBrowserAction(OIDCConfiguration config) async {
    final baseUri = _parseUri(AppConfig.baseUrl);
    if (baseUri != null) {
      consumeState(_authenticateOidcOnBrowserInteractor.execute(baseUri, config));
    } else {
      loginState.value = LoginState(Left(LoginCanNotAuthenticationSSOAction()));
    }
  }

  void _getTokenOIDCSuccess(GetTokenOIDCSuccess success) {
    log('LoginController::_getTokenOIDCSuccess(): ${success.tokenOIDC.toString()}');
    loginState.value = LoginState(Right(success));
    _dynamicUrlInterceptors.changeBaseUrl(kIsWeb ? AppConfig.baseUrl : _urlText);
    authorizationInterceptors.setTokenAndAuthorityOidc(
        newToken: success.tokenOIDC.toToken(),
        newConfig: success.configuration);
    authorizationIsolateInterceptors.setTokenAndAuthorityOidc(
        newToken: success.tokenOIDC.toToken(),
        newConfig: success.configuration);
    pushAndPop(AppRoutes.session, arguments: _dynamicUrlInterceptors.baseUrl);
  }

  void _loginAction(Uri baseUrl, UserName userName, Password password) async {
    consumeState(_authenticationInteractor.execute(baseUrl, userName, password));
  }

  void _loginSuccessAction(AuthenticationUserSuccess success) {
    loginState.value = LoginState(Right(success));
    _dynamicUrlInterceptors.changeBaseUrl(kIsWeb ? AppConfig.baseUrl : _urlText);
    authorizationInterceptors.setBasicAuthorization(_userNameText, _passwordText);
    authorizationIsolateInterceptors.setBasicAuthorization(_userNameText, _passwordText);
    pushAndPop(AppRoutes.session, arguments: _dynamicUrlInterceptors.baseUrl);
  }

  void _loginFailureAction(FeatureFailure failure) {
    logError('LoginController::_loginFailureAction(): $failure');
    loginState.value = LoginState(Left(failure));
  }

  void formatUrl(String url) {
    log('LoginController::formatUrl(): $url');
    if (url.isValid()) {
      urlInputController.text = url.removePrefix();
    }
    setUrlText(urlInputController.text);
  }

  void _saveRecentLoginUrl() {
    if (_urlText?.isNotEmpty == true && !BuildUtils.isWeb) {
      final recentLoginUrl = RecentLoginUrl.now(_urlText!);
      log('LoginController::_saveRecentLoginUrl(): $recentLoginUrl');
      consumeState(_saveLoginUrlOnMobileInteractor.execute(recentLoginUrl));
    }
  }

  Future<List<RecentLoginUrl>> getAllRecentLoginUrlAction(String pattern) async {
    return await _getAllRecentLoginUrlOnMobileInteractor
        .execute(pattern: pattern)
        .then((result) => result.fold(
            (failure) => <RecentLoginUrl>[],
            (success) => success is GetAllRecentLoginUrlLatestSuccess
                ? success.listRecentLoginUrl
                : <RecentLoginUrl>[]
        ));
  }

  void setUsername(String username) {
    log('LoginController::formatUsername(): $username');
    usernameInputController.text = username;
    setUserNameText(usernameInputController.text);
  }

  void _saveRecentLoginUsername() {
    if(BuildUtils.isWeb || _userNameText == null || _userNameText!.isEmpty || !_userNameText!.isEmail) {
      return ;
    }
    final recentLoginUsername = RecentLoginUsername.now(_userNameText!);
    log('LoginController::_saveRecentLoginUsername(): $recentLoginUsername');
    consumeState(_saveLoginUsernameOnMobileInteractor.execute(recentLoginUsername));
  }

  Future<List<RecentLoginUsername>> getAllRecentLoginUsernameAction(String pattern) async {
    return await _getAllRecentLoginUsernameOnMobileInteractor
        .execute(pattern: pattern)
        .then((result) => result.fold(
            (failure) => <RecentLoginUsername>[],
            (success) => success is GetAllRecentLoginUsernameLatestSuccess
                ? success.listRecentLoginUsername
                : <RecentLoginUsername>[]
        ));
  }

  @override
  void onClose() {
    urlInputController.clear();
    super.onClose();
  }

  @override
  void onDone() {}
}