import 'dart:async';

import 'package:auth/src/features/sign_up/domain/entities/address_entity.dart';
import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart';
import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart';
import 'package:auth/src/features/sign_up/domain/generate_two_fa_sign_up_use_case.dart';
import 'package:auth/src/features/sign_up/domain/sign_up_use_case.dart';
import 'package:auth/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case.dart';
import 'package:auth/src/features/sign_up/presentation/providers/generate_two_fa_sign_up_provider.dart';
import 'package:auth/src/features/sign_up/presentation/providers/sign_up_provider.dart';
import 'package:auth/src/features/sign_up/presentation/providers/verify_two_fa_code_sign_up_provider.dart';
import 'package:auth/src/features/sign_up/presentation/state/address_view_state.dart';
import 'package:auth/src/features/sign_up/presentation/state/sign_up_view_state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:uuid/uuid.dart';

final signUpViewModelProvider =
    NotifierProvider.autoDispose<SignUpViewModel, SignUpViewState>(
      SignUpViewModel.new,
    );

class SignUpViewModel extends Notifier<SignUpViewState> {
  late final SignUpUseCase _signUpUseCase;
  late final GenerateTwoFASignUpUseCase _generateTwoFASignUpUseCase;
  late final VerifyTwoFACodeSignUpUseCase _verifyTwoFACodeSignUpUseCase;

  late final TextEditingController firstNameController;
  late final TextEditingController lastNameController;
  late final TextEditingController documentNumberController;
  late final TextEditingController documentTypeController;
  late final TextEditingController phoneController;
  late final TextEditingController emailController;

  late final TextEditingController relationshipController;
  late final TextEditingController bornAtController;
  late final TextEditingController placeOfBirthController;
  late final TextEditingController birthCountryController;

  late final TextEditingController addressStreetController;
  late final TextEditingController addressCityController;
  late final TextEditingController addressProvinceController;
  late final TextEditingController addressStateController;
  late final TextEditingController addressCountryController;
  late final TextEditingController addressPostCodeController;

  late final TextEditingController passwordController;
  late final TextEditingController repeatPasswordController;

  static const int _lastIndex = 2;

  static final RegExp _emailRegex = RegExp(
    r'^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$',
    caseSensitive: false,
  );

  static final RegExp _phoneRegex = RegExp(r'^\+?\d{6,15}$');

  @override
  SignUpViewState build() {
    _signUpUseCase = ref.read(signUpUseCaseProvider);
    _generateTwoFASignUpUseCase = ref.read(generateTwoFASignUpUseCaseProvider);
    _verifyTwoFACodeSignUpUseCase = ref.read(
      verifyTwoFACodeSignUpUseCaseProvider,
    );
    final initial = SignUpViewState(userId: const Uuid().v4());
    _initControllers(initial);
    _addListeners();

    ref.onDispose(disposeControllers);

    return initial;
  }

  void _initControllers(SignUpViewState s) {
    firstNameController = TextEditingController(text: s.firstName);
    lastNameController = TextEditingController(text: s.lastName);
    documentNumberController = TextEditingController(text: s.documentNumber);
    documentTypeController = TextEditingController(text: s.documentType);
    phoneController = TextEditingController(text: s.phone);
    emailController = TextEditingController(text: s.email);

    relationshipController = TextEditingController(text: s.relationship);

    bornAtController = TextEditingController(
      text: s.bornAt == null ? '' : _formatDate(s.bornAt!),
    );

    placeOfBirthController = TextEditingController(text: s.placeOfBirth);
    birthCountryController = TextEditingController(text: s.birthCountry);

    addressStreetController = TextEditingController(text: s.address.street);
    addressCityController = TextEditingController(text: s.address.city);
    addressProvinceController = TextEditingController(text: s.address.province);
    addressStateController = TextEditingController(text: s.address.state);
    addressCountryController = TextEditingController(text: s.address.country);
    addressPostCodeController = TextEditingController(
      text: s.address.postCode?.toString() ?? '',
    );

    passwordController = TextEditingController(text: s.password);
    repeatPasswordController = TextEditingController(text: s.repeatPassword);
  }

  void _addListeners() {
    firstNameController.addListener(_onFirstNameChanged);
    lastNameController.addListener(_onLastNameChanged);
    documentNumberController.addListener(_onDocumentNumberChanged);
    documentTypeController.addListener(_onDocumentTypeChanged);
    phoneController.addListener(_onPhoneChanged);
    emailController.addListener(_onEmailChanged);

    relationshipController.addListener(_onRelationshipChanged);
    bornAtController.addListener(_onBornAtTextChanged);
    placeOfBirthController.addListener(_onPlaceOfBirthChanged);
    birthCountryController.addListener(_onBirthCountryChanged);

    addressStreetController.addListener(_onAddressStreetChanged);
    addressCityController.addListener(_onAddressCityChanged);
    addressProvinceController.addListener(_onAddressProvinceChanged);
    addressStateController.addListener(_onAddressStateChanged);
    addressCountryController.addListener(_onAddressCountryChanged);
    addressPostCodeController.addListener(_onAddressPostCodeChanged);

    passwordController.addListener(_onPasswordChanged);
    repeatPasswordController.addListener(_onRepeatPasswordChanged);
  }

  void next() {
    if (state.isLoading) return;

    final ok = switch (state.currentIndex) {
      0 => _validateStep0(),
      1 => _validateStep1(),
      2 => _validateStep2(),
      _ => true,
    };

    if (!ok) return;

    if (state.currentIndex >= _lastIndex) {
      unawaited(signUp());
      return;
    }

    state = state.copyWith(
      currentIndex: (state.currentIndex + 1).clamp(0, _lastIndex),
    );
  }

  void back() {
    if (state.isLoading) return;
    if (state.currentIndex <= 0) return;

    state = state.copyWith(
      currentIndex: (state.currentIndex - 1).clamp(0, _lastIndex),
    );
  }

  void updateDialCode(String dialCode) {
    state = state.copyWith(dialCode: dialCode);
  }

  void setBirthCountryFromPicker({required String name, required String code}) {
    birthCountryController.text = name;
    state = state.copyWith(birthCountry: name, birthCountryCode: code);
  }

  Future<void> pickBornAt(BuildContext context) async {
    FocusManager.instance.primaryFocus?.unfocus();

    final now = DateTime.now();
    final initial = state.bornAt ?? DateTime(now.year - 18, now.month, now.day);

    final safeInitial = initial.isAfter(now) ? now : initial;

    final picked = await showDatePicker(
      context: context,
      initialDate: safeInitial,
      firstDate: DateTime(1900, 1, 1),
      lastDate: now,
    );

    if (!ref.mounted) return;
    if (picked == null) return;

    setBornAt(picked);
  }

  void setDocumentType(String? value) {
    final v = value ?? '';
    if (documentTypeController.text == v) return;
    documentTypeController.text = v;
  }

  void setRelationship(String? value) {
    final v = value ?? '';
    if (relationshipController.text == v) return;
    relationshipController.text = v;
  }

  void setAddressCountry({required String name}) {
    addressCountryController.text = name;
    state = state.copyWith(address: state.address.copyWith(country: name));
  }

  void setAcceptTerms(bool value) {
    if (value == state.acceptTerms) return;
    state = state.copyWith(acceptTerms: value, errorMessage: '');
  }

  void toggleShowPassword() {
    state = state.copyWith(isShowPassword: !state.isShowPassword);
  }

  void _onFirstNameChanged() {
    final text = firstNameController.text;
    if (text == state.firstName) return;
    state = state.copyWith(firstName: text, errorMessage: '');
  }

  void _onLastNameChanged() {
    final text = lastNameController.text;
    if (text == state.lastName) return;
    state = state.copyWith(lastName: text, errorMessage: '');
  }

  void _onDocumentNumberChanged() {
    final text = documentNumberController.text;
    if (text == state.documentNumber) return;
    state = state.copyWith(documentNumber: text, errorMessage: '');
  }

  void _onDocumentTypeChanged() {
    final text = documentTypeController.text;
    if (text == state.documentType) return;
    state = state.copyWith(documentType: text, errorMessage: '');
  }

  void _onRelationshipChanged() {
    final text = relationshipController.text;
    if (text == state.relationship) return;
    state = state.copyWith(relationship: text, errorMessage: '');
  }

  void _onPhoneChanged() {
    final text = phoneController.text;
    if (text == state.phone) return;

    state = state.copyWith(phone: text);

    if (state.showErrors) {
      state = state.copyWith(phoneError: _phoneErrorFor(text));
    }
  }

  void _onEmailChanged() {
    final text = emailController.text;
    if (text == state.email) return;

    state = state.copyWith(email: text, errorMessage: '');

    if (state.showErrors) {
      state = state.copyWith(emailError: _emailErrorFor(text));
    }
  }

  void _onPasswordChanged() {
    final text = passwordController.text;
    if (text == state.password) return;

    state = state.copyWith(password: text, errorMessage: '');

    if (state.showErrors) {
      state = state.copyWith(
        passwordError: _passwordErrorFor(
          password: state.password,
          repeatPassword: state.repeatPassword,
        ),
      );
    }
  }

  void _onRepeatPasswordChanged() {
    final text = repeatPasswordController.text;
    if (text == state.repeatPassword) return;

    state = state.copyWith(repeatPassword: text, errorMessage: '');

    if (state.showErrors) {
      state = state.copyWith(
        passwordError: _passwordErrorFor(
          password: state.password,
          repeatPassword: state.repeatPassword,
        ),
      );
    }
  }

  void _onBornAtTextChanged() {
    final text = bornAtController.text;
    final parsed = _tryParseDate(text);

    if (text.trim().isEmpty) {
      if (state.bornAt != null) {
        state = state.copyWith(bornAt: null, errorMessage: '');
      }
      return;
    }

    if (parsed != null && parsed != state.bornAt) {
      state = state.copyWith(bornAt: parsed, errorMessage: '');
    }
  }

  void _onPlaceOfBirthChanged() {
    final text = placeOfBirthController.text;
    if (text == state.placeOfBirth) return;
    state = state.copyWith(placeOfBirth: text, errorMessage: '');
  }

  void _onBirthCountryChanged() {
    final text = birthCountryController.text;
    if (text == state.birthCountry) return;
    state = state.copyWith(birthCountry: text, errorMessage: '');
  }

  void _onAddressStreetChanged() {
    final text = addressStreetController.text;
    if (text == state.address.street) return;
    state = state.copyWith(
      address: state.address.copyWith(street: text),
      errorMessage: '',
    );
  }

  void _onAddressCityChanged() {
    final text = addressCityController.text;
    if (text == state.address.city) return;
    state = state.copyWith(
      address: state.address.copyWith(city: text),
      errorMessage: '',
    );
  }

  void _onAddressProvinceChanged() {
    final text = addressProvinceController.text;
    if (text == state.address.province) return;
    state = state.copyWith(
      address: state.address.copyWith(province: text),
      errorMessage: '',
    );
  }

  void _onAddressStateChanged() {
    final text = addressStateController.text;
    if (text == state.address.state) return;
    state = state.copyWith(
      address: state.address.copyWith(state: text),
      errorMessage: '',
    );
  }

  void _onAddressCountryChanged() {
    final text = addressCountryController.text;
    if (text == state.address.country) return;
    state = state.copyWith(
      address: state.address.copyWith(country: text),
      errorMessage: '',
    );
  }

  void _onAddressPostCodeChanged() {
    final text = addressPostCodeController.text.trim();
    final parsed = int.tryParse(text);

    if (text.isEmpty) {
      if (state.address.postCode != null) {
        state = state.copyWith(
          address: state.address.copyWith(postCode: null),
          errorMessage: '',
        );
      }
      return;
    }

    if (parsed != null && parsed != state.address.postCode) {
      state = state.copyWith(
        address: state.address.copyWith(postCode: parsed),
        errorMessage: '',
      );
    }
  }

  void setOtpCode(String code) {
    state = state.copyWith(otpCode: code, otpError: '');
  }

  bool _validateStep0() {
    final emailError = _emailErrorFor(state.email);
    final phoneError = _phoneErrorFor(state.phone);
    state = state.copyWith(
      showErrors: true,
      emailError: emailError,
      phoneError: phoneError,
      errorMessage: '',
    );
    if (state.firstName.trim().isEmpty) {
      state = state.copyWith(errorMessage: 'El nombre es obligatorio');
      return false;
    }
    if (state.lastName.trim().isEmpty) {
      state = state.copyWith(errorMessage: 'El apellido es obligatorio');
      return false;
    }
    if (state.documentType.trim().isEmpty) {
      state = state.copyWith(
        errorMessage: 'El tipo de documento es obligatorio',
      );
      return false;
    }
    if (state.documentNumber.trim().isEmpty) {
      state = state.copyWith(
        errorMessage: 'El número de documento es obligatorio',
      );
      return false;
    }
    if (phoneError.isNotEmpty) {
      state = state.copyWith(errorMessage: phoneError);
      return false;
    }
    if (emailError.isNotEmpty) {
      state = state.copyWith(errorMessage: emailError);
      return false;
    }
    if (!state.acceptTerms) {
      state = state.copyWith(
        errorMessage: 'Debes aceptar los términos y condiciones',
      );
      return false;
    }
    return true;
  }

  bool _validateStep1() {
    if (!state.showErrors) {
      state = state.copyWith(showErrors: true, errorMessage: '');
    } else {
      state = state.copyWith(errorMessage: '');
    }
    if (state.bornAt == null) {
      state = state.copyWith(
        errorMessage: 'Selecciona una fecha de nacimiento válida (DD/MM/AAAA)',
      );
      return false;
    }
    if (state.relationship.trim().isEmpty) {
      state = state.copyWith(errorMessage: 'La relación es obligatoria');
      return false;
    }

    if (state.placeOfBirth.trim().isEmpty) {
      state = state.copyWith(errorMessage: 'Falta el lugar de nacimiento');
      return false;
    }

    if (state.birthCountry.trim().isEmpty) {
      state = state.copyWith(errorMessage: 'Falta el país de nacimiento');
      return false;
    }

    if (!_isAddressValid(state.address)) {
      state = state.copyWith(errorMessage: 'Completa la dirección');
      return false;
    }

    return true;
  }

  bool _validateStep2() {
    final passwordError = _passwordErrorFor(
      password: state.password,
      repeatPassword: state.repeatPassword,
    );

    state = state.copyWith(
      showErrors: true,
      passwordError: passwordError,
      errorMessage: '',
    );

    if (passwordError.isNotEmpty) return false;

    return true;
  }

  bool _validateForm() {
    return _validateStep0() && _validateStep1() && _validateStep2();
  }

  bool _isValidEmail(String email) => _emailRegex.hasMatch(email);

  String _emailErrorFor(String value) {
    final email = value.trim();
    if (email.isEmpty) return I18n.errorEmailRequired;
    if (!_isValidEmail(email)) return I18n.errorEmailInvalid;
    return '';
  }

  String _passwordErrorFor({
    required String password,
    required String repeatPassword,
  }) {
    final p = password.trim();
    final rp = repeatPassword.trim();

    if (p.isEmpty) return I18n.errorPasswordRequired;
    if (p.length < 6) return I18n.errorPasswordMinLength;

    if (rp.isEmpty) return 'Repite la contraseña';
    if (p != rp) return 'Las contraseñas no coinciden';

    return '';
  }

  String _phoneErrorFor(String value) {
    final phone = value.trim();
    if (phone.isEmpty) return 'El teléfono es obligatorio';
    if (!_phoneRegex.hasMatch(phone)) {
      return 'El teléfono no tiene un formato válido';
    }
    return '';
  }

  bool _isAddressValid(AddressViewState a) {
    return a.street.trim().isNotEmpty &&
        a.city.trim().isNotEmpty &&
        a.province.trim().isNotEmpty &&
        a.state.trim().isNotEmpty &&
        a.country.trim().isNotEmpty &&
        a.postCode != null;
  }

  AddressEntity _toAddressEntity(AddressViewState a) {
    return AddressEntity(
      street: a.street.trim(),
      city: a.city.trim(),
      province: a.province.trim(),
      state: a.state.trim(),
      country: a.country.trim(),
      postCode: a.postCode ?? 0,
    );
  }

  SignUpRequestEntity _toRequest() {
    final bornAt = state.bornAt;
    if (bornAt == null) throw Exception('bornAt is required');

    return SignUpRequestEntity(
      documentType: state.documentType.trim(),
      documentNumber: state.documentNumber.trim(),
      relationship: state.relationship.trim(),
      firstName: state.firstName.trim(),
      lastName: state.lastName.trim(),
      email: state.email.trim(),
      phone: state.dialCode.trim() + state.phone.trim(),
      language: state.language.trim().isEmpty ? 'es' : state.language.trim(),
      password: state.password,
      bornAt: bornAt.millisecondsSinceEpoch,
      userId: state.userId.trim(),
      placeOfBirth: state.placeOfBirth.trim(),
      birthCountry: state.birthCountryCode.trim(),
      // birthCountry: 'spain',
      addresses: <AddressEntity>[_toAddressEntity(state.address)],
      taxResidences: <AddressEntity>[_toAddressEntity(state.address)],
    );
  }

  Future<bool> signUp() async {
    if (state.isLoading) return false;
    if (!_validateForm()) return false;

    _startLoadingForSignUp();

    try {
      final request = _toRequest();

      final token = await _signUp(request);
      if (!ref.mounted) return false;

      final secretEntity = await _generateTwoFA(token);
      if (!ref.mounted) return false;

      final validationError = _twoFAResponseError(secretEntity);
      if (validationError != null) {
        _finishWithError(token: token, message: validationError);
        return false;
      }
      state = state.copyWith(showSecretCode: true);
      _finishWithSuccess(token: token, twoFASecret: secretEntity);
      return true;
    } catch (e) {
      if (!ref.mounted) return false;
      _finishWithError(message: e.toString());
      return false;
    }
  }

  void _startLoadingForSignUp() {
    state = state.copyWith(
      isLoading: true,
      errorMessage: '',
      twoFASecret: null,
      showSecretCode: false,
      showAccountCreated: false,
    );
  }

  Future<String> _signUp(SignUpRequestEntity request) async {
    return _signUpUseCase.signUp(request: request);
  }

  Future<TwoFASecretEntity> _generateTwoFA(String token) async {
    return _generateTwoFASignUpUseCase.generateTwoFASignUp(token: token);
  }

  Future<bool> verifyTwoFACodeSignUp({required String token}) async {
    if (state.isOtpLoading) return false;

    final code = state.otpCode.trim();
    if (code.length != 6) {
      state = state.copyWith(otpError: 'error otp');
      return false;
    }

    state = state.copyWith(isOtpLoading: true, otpError: '');

    try {
      await _verifyTwoFACodeSignUpUseCase.verifyTwoFACodeSignUp(
        token: token,
        code: code,
      );

      if (!ref.mounted) return false;
      state = state.copyWith(isOtpLoading: false);
      return true;
    } catch (e) {
      if (!ref.mounted) return false;
      state = state.copyWith(isOtpLoading: false, otpError: e.toString());
      return false;
    }
  }

  String? _twoFAResponseError(TwoFASecretEntity entity) {
    if (!entity.isCreated) {
      return 'No se pudo generar el 2FA (isCreated=false)';
    }

    final secret = entity.item.secret.trim();
    final qr = entity.item.qr.trim();

    if (secret.isEmpty || qr.isEmpty) {
      return 'Respuesta inválida del 2FA (secret/qr vacío)';
    }

    return null;
  }

  void _finishWithSuccess({
    required String token,
    required TwoFASecretEntity twoFASecret,
  }) {
    state = state.copyWith(
      isLoading: false,
      token: token,
      twoFASecret: twoFASecret,
      errorMessage: '',
    );
  }

  void _finishWithError({String? token, required String message}) {
    state = state.copyWith(
      isLoading: false,
      token: token ?? state.token,
      errorMessage: message,
    );
  }

  void showAccountCreated() {
    state = state.copyWith(showAccountCreated: true, showSecretCode: false);
  }

  DateTime? _tryParseDate(String value) {
    final v = value.trim();
    if (v.isEmpty) return null;

    final parts = v.split('/');
    if (parts.length != 3) return null;

    final d = int.tryParse(parts[0]);
    final m = int.tryParse(parts[1]);
    final y = int.tryParse(parts[2]);

    if (d == null || m == null || y == null) return null;

    final date = DateTime(y, m, d);
    if (date.year != y || date.month != m || date.day != d) return null;

    return date;
  }

  String _formatDate(DateTime date) {
    final dd = date.day.toString().padLeft(2, '0');
    final mm = date.month.toString().padLeft(2, '0');
    final yyyy = date.year.toString();
    return '$dd/$mm/$yyyy';
  }

  void setBornAt(DateTime date) {
    bornAtController.text = _formatDate(date);
    state = state.copyWith(bornAt: date, errorMessage: '');
  }

  void disposeControllers() {
    firstNameController.removeListener(_onFirstNameChanged);
    lastNameController.removeListener(_onLastNameChanged);
    documentNumberController.removeListener(_onDocumentNumberChanged);
    documentTypeController.removeListener(_onDocumentTypeChanged);
    phoneController.removeListener(_onPhoneChanged);
    emailController.removeListener(_onEmailChanged);

    relationshipController.removeListener(_onRelationshipChanged);
    bornAtController.removeListener(_onBornAtTextChanged);
    placeOfBirthController.removeListener(_onPlaceOfBirthChanged);
    birthCountryController.removeListener(_onBirthCountryChanged);

    addressStreetController.removeListener(_onAddressStreetChanged);
    addressCityController.removeListener(_onAddressCityChanged);
    addressProvinceController.removeListener(_onAddressProvinceChanged);
    addressStateController.removeListener(_onAddressStateChanged);
    addressCountryController.removeListener(_onAddressCountryChanged);
    addressPostCodeController.removeListener(_onAddressPostCodeChanged);

    passwordController.removeListener(_onPasswordChanged);
    repeatPasswordController.removeListener(_onRepeatPasswordChanged);

    firstNameController.dispose();
    lastNameController.dispose();
    documentNumberController.dispose();
    documentTypeController.dispose();
    phoneController.dispose();
    emailController.dispose();

    relationshipController.dispose();
    bornAtController.dispose();
    placeOfBirthController.dispose();
    birthCountryController.dispose();

    addressStreetController.dispose();
    addressCityController.dispose();
    addressProvinceController.dispose();
    addressStateController.dispose();
    addressCountryController.dispose();
    addressPostCodeController.dispose();

    passwordController.dispose();
    repeatPasswordController.dispose();
  }
}
