import 'dart:async';

import 'package:firebase_auth/firebase_auth.dart' hide User;
import 'package:firebase_data_connect/firebase_data_connect.dart';
import 'package:krow_data_connect/krow_data_connect.dart' as dc;

import '../../domain/entities/tax_form_entity.dart';
import '../../domain/repositories/tax_forms_repository.dart';

class TaxFormsRepositoryImpl implements TaxFormsRepository {
  TaxFormsRepositoryImpl({
    required this.firebaseAuth,
    required this.dataConnect,
  });

  final FirebaseAuth firebaseAuth;
  final dc.ExampleConnector dataConnect;

  String? _staffId;

  Future<String> _getStaffId() async {
    if (_staffId != null) return _staffId!;

    final user = firebaseAuth.currentUser;
    if (user == null) {
      throw Exception('User not logged in');
    }

    final result =
        await dataConnect.getStaffByUserId(userId: user.uid).execute();
    final staffs = result.data.staffs;
    if (staffs.isEmpty) {
      // This might happen if the user is logged in but staff profile isn't created yet or not synced.
      // For now, fail hard. Code can be improved to handle this case properly.
      throw Exception('No staff profile found for user');
    }

    _staffId = staffs.first.id;
    return _staffId!;
  }

  @override
  Future<List<TaxFormEntity>> getTaxForms() async {
    final staffId = await _getStaffId();
    final result =
        await dataConnect.getTaxFormsBystaffId(staffId: staffId).execute();

    final forms = result.data.taxForms.map((e) => _mapToEntity(e)).toList();

    // Check if required forms exist, create if not.
    final typesPresent = forms.map((f) => f.type).toSet();
    bool createdNew = false;

    if (!typesPresent.contains(TaxFormType.i9)) {
      await _createInitialForm(staffId, TaxFormType.i9);
      createdNew = true;
    }
    if (!typesPresent.contains(TaxFormType.w4)) {
      await _createInitialForm(staffId, TaxFormType.w4);
      createdNew = true;
    }

    if (createdNew) {
      final result2 =
          await dataConnect.getTaxFormsBystaffId(staffId: staffId).execute();
      return result2.data.taxForms.map((e) => _mapToEntity(e)).toList();
    }

    return forms;
  }

  Future<void> _createInitialForm(String staffId, TaxFormType type) async {
    String title = '';
    String subtitle = '';
    String description = '';

    if (type == TaxFormType.i9) {
      title = 'Form I-9';
      subtitle = 'Employment Eligibility Verification';
      description = 'Required for all new hires to verify identity.';
    } else {
      title = 'Form W-4';
      subtitle = 'Employee\'s Withholding Certificate';
      description = 'Determines federal income tax withholding.';
    }

    await dataConnect
        .createTaxForm(
          staffId: staffId,
          formType: _mapTypeToGenerated(type),
          title: title,
        )
        .subtitle(subtitle)
        .description(description)
        .status(dc.TaxFormStatus.NOT_STARTED)
        .execute();
  }

  @override
  Future<void> submitForm(TaxFormType type, Map<String, dynamic> data) async {
    final staffId = await _getStaffId();
    final result =
        await dataConnect.getTaxFormsBystaffId(staffId: staffId).execute();
    final targetTypeString = _mapTypeToGenerated(type).name;

    final form = result.data.taxForms.firstWhere(
      (e) => e.formType.stringValue == targetTypeString,
      orElse: () => throw Exception('Form not found for submission'),
    );

    // AnyValue expects a scalar, list, or map.
    await dataConnect
        .updateTaxForm(
          id: form.id,
        )
        .formData(AnyValue.fromJson(data))
        .status(dc.TaxFormStatus.SUBMITTED)
        .execute();
  }

  @override
  Future<void> updateFormStatus(TaxFormType type, TaxFormStatus status) async {
    final staffId = await _getStaffId();
    final result =
        await dataConnect.getTaxFormsBystaffId(staffId: staffId).execute();
    final targetTypeString = _mapTypeToGenerated(type).name;

    final form = result.data.taxForms.firstWhere(
      (e) => e.formType.stringValue == targetTypeString,
      orElse: () => throw Exception('Form not found for update'),
    );

    await dataConnect
        .updateTaxForm(
          id: form.id,
        )
        .status(_mapStatusToGenerated(status))
        .execute();
  }

  TaxFormEntity _mapToEntity(dc.GetTaxFormsBystaffIdTaxForms form) {
    return TaxFormEntity(
      type: _mapTypeFromString(form.formType.stringValue),
      title: form.title,
      subtitle: form.subtitle ?? '',
      description: form.description ?? '',
      status: _mapStatusFromString(form.status.stringValue),
      lastUpdated: form.updatedAt?.toDateTime(),
    );
  }

  TaxFormType _mapTypeFromString(String value) {
    switch (value) {
      case 'I9':
        return TaxFormType.i9;
      case 'W4':
        return TaxFormType.w4;
      default:
        // Handle unexpected types gracefully if needed, or throw.
        // Assuming database integrity for now.
        if (value == 'I-9') return TaxFormType.i9; // Fallback just in case
        throw Exception('Unknown TaxFormType string: $value');
    }
  }

  TaxFormStatus _mapStatusFromString(String value) {
    switch (value) {
      case 'NOT_STARTED':
        return TaxFormStatus.notStarted;
      case 'DRAFT':
        return TaxFormStatus.inProgress;
      case 'SUBMITTED':
        return TaxFormStatus.submitted;
      case 'APPROVED':
        return TaxFormStatus.approved;
      case 'REJECTED':
        return TaxFormStatus.rejected;
      default:
        return TaxFormStatus.notStarted; // Default fallback
    }
  }

  dc.TaxFormType _mapTypeToGenerated(TaxFormType type) {
    switch (type) {
      case TaxFormType.i9:
        return dc.TaxFormType.I9;
      case TaxFormType.w4:
        return dc.TaxFormType.W4;
    }
  }

  dc.TaxFormStatus _mapStatusToGenerated(TaxFormStatus status) {
    switch (status) {
      case TaxFormStatus.notStarted:
        return dc.TaxFormStatus.NOT_STARTED;
      case TaxFormStatus.inProgress:
        return dc.TaxFormStatus.DRAFT;
      case TaxFormStatus.submitted:
        return dc.TaxFormStatus.SUBMITTED;
      case TaxFormStatus.approved:
        return dc.TaxFormStatus.APPROVED;
      case TaxFormStatus.rejected:
        return dc.TaxFormStatus.REJECTED;
    }
  }
}
