import { Component, DestroyRef, ElementRef, computed, inject, signal, viewChild } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import {
  FormBuilder,
  ReactiveFormsModule,
  Validators
} from '@angular/forms';
import { ToastrService } from 'ngx-toastr';
import { Subject, catchError, finalize, map, of, switchMap } from 'rxjs';

import {
  CountryDto,
  CountryModalMode,
  CreateCountryRequest,
  UpdateCountryRequest
} from '../../models/country.model';
import { CurrencyLookupDto, CurrencyService } from '../../../currencies/public-api';
import { CountryService } from '../../data-access/country.service';
import { DataTableQueryState } from '../../../../../shared/components/data-table/data-table-query.state';
import {
  DataTableAction,
  DataTableActionEvent,
  DataTableColumn,
  DataTablePageEvent,
  DataTableQuery,
  DataTableRecord,
  DataTableSortEvent
} from '../../../../../shared/components/data-table/data-table.types';
import { DataTable } from '../../../../../shared/components/data-table/data-table';
import { DataTableCellDirective } from '../../../../../shared/directives/data-table-cell.directive';
import { Autocomplete } from '../../../../../shared/components/form/autocomplete/autocomplete';
import {
  AutocompleteDisplayFn,
  AutocompleteSearchFn,
  AutocompleteValueFn
} from '../../../../../shared/components/form/autocomplete/autocomplete.types';
import { FormInput } from '../../../../../shared/components/form/form-input/form-input';
import { Modal } from '../../../../../shared/components/modal/modal';
import { ConfirmDialog } from '../../../../../shared/components/confirm-dialog/confirm-dialog';

interface CountryTableRow extends DataTableRecord {
  id: string;
  iso2: string;
  iso3: string;
  name: string;
  phoneCode: string | null;
  defaultCurrencyId: string | null;
  isActive: boolean;
  serialNumber: number;
  createdOn?: string;
  modifiedOn?: string | null;
}

@Component({
  selector: 'country-list',
  standalone: true,
  imports: [DataTable, DataTableCellDirective, Modal, ReactiveFormsModule, FormInput, Autocomplete, ConfirmDialog],
  templateUrl: './country-list.html',
  styleUrl: './country-list.scss',
})
export class CountryList {
  private readonly destroyRef = inject(DestroyRef);
  private readonly countryApi = inject(CountryService);
  private readonly currencyApi = inject(CurrencyService);
  private readonly formBuilder = inject(FormBuilder);
  private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);
  private readonly toastr = inject(ToastrService);
  private readonly countryQueryRequests$ = new Subject<DataTableQuery>();

  readonly queryState = new DataTableQueryState();

  readonly countries = signal<CountryTableRow[]>([]);
  readonly totalRecords = signal(0);
  readonly filteredRecords = signal(0);
  readonly saving = signal(false);

  readonly showCountryModal = signal(false);
  readonly countryModalMode = signal<CountryModalMode>('create');
  readonly selectedCountryId = signal<string | null>(null);
  readonly selectedCountry = signal<CountryDto | null>(null);
  readonly selectedCurrency = signal<CurrencyLookupDto | null>(null);
  readonly countrySubmitAttempted = signal(false);
  readonly pendingDeleteCountry = signal<CountryDto | null>(null);
  readonly deleteConfirmDialog = viewChild(ConfirmDialog);

  readonly countryForm = this.formBuilder.nonNullable.group({
    name: [
      '',
      [
        Validators.required,
        Validators.maxLength(150)
      ]
    ],
    iso2: [
      '',
      [
        Validators.required,
        Validators.pattern(/^[A-Za-z]{2}$/)
      ]
    ],
    iso3: [
      '',
      [
        Validators.required,
        Validators.pattern(/^[A-Za-z]{3}$/)
      ]
    ],
    phoneCode: [
      '',
      [
        Validators.maxLength(16),
        Validators.pattern(/^\+?[0-9\- ]{1,15}$/)
      ]
    ],
    defaultCurrencyId: this.formBuilder.control<string | null>(null)
  });

  readonly searchCurrencies: AutocompleteSearchFn<CurrencyLookupDto> =
    (term, limit) => this.currencyApi.autocomplete(term, limit);
  readonly displayCurrency: AutocompleteDisplayFn<CurrencyLookupDto> = currency => {
    const baseLabel = [currency.code, currency.name].filter(Boolean).join(' - ');

    return currency.symbol?.trim()
      ? `${baseLabel} (${currency.symbol})`
      : baseLabel;
  };
  readonly currencyValue: AutocompleteValueFn<CurrencyLookupDto, string> = currency => currency.id;

  readonly countryModalTitle = computed(() =>
    this.countryModalMode() === 'create'
      ? 'Add Country'
      : 'Edit Country'
  );

  readonly countrySubmitLabel = computed(() =>
    this.countryModalMode() === 'create'
      ? 'Save'
      : 'Update'
  );

  readonly countryLoadingLabel = computed(() =>
    this.countryModalMode() === 'create'
      ? 'Saving...'
      : 'Updating...'
  );

  readonly countrySubmitAction = computed<'save' | 'update'>(() =>
    this.countryModalMode() === 'create'
      ? 'save'
      : 'update'
  );

  readonly columns = signal<DataTableColumn<CountryTableRow>[]>([
    { key: 'serialNumber', label: 'Sr. No.', header: 'Sr.No.', sortable: false, width: '100px' },
    { key: 'name', label: 'Name', header: 'Name', sortable: true, align: 'left' },
    { key: 'iso2', label: 'ISO2', header: 'ISO2', sortable: true },
    { key: 'iso3', label: 'ISO3', header: 'ISO3', sortable: true },
    { key: 'phoneCode', label: 'Phone Code', header: 'Phone Code', sortable: true },
    { key: 'currencyName', label: 'Default Currency', header: 'Default Currency', sortable: true, align: 'left' },
    {
      key: 'isActive',
      label: 'Status',
      header: 'Status',
      sortable: true,
      badge: true,
      badgeClass: value =>
        value === true
          ? 'badge bg-success/10 text-success'
          : 'badge bg-danger/10 text-danger',
      formatter: value => value ? 'Active' : 'Inactive'
    }
  ]);

  readonly actions = signal<DataTableAction<CountryTableRow>[]>([
    {
      type: 'edit',
      label: 'Edit',
      icon: 'ti ti-edit',
      className: 'text-primary'
    },
    {
      type: 'delete',
      label: 'Delete',
      icon: 'ti ti-trash',
      className: 'text-danger',
      visible: row => row.isActive
    },
    {
      type: 'activate',
      label: 'Activate',
      icon: 'ti ti-check',
      className: 'text-success',
      visible: row => !row.isActive
    }
  ]);

  constructor() {
    this.countryQueryRequests$
      .pipe(
        switchMap(query =>
          this.countryApi.getCountryDataTable(query).pipe(
            catchError(() => {
              this.toastr.error('Unable to load countries.');
              this.clearCountryGrid();
              return of(null);
            })
          )
        ),
        takeUntilDestroyed(this.destroyRef)
      )
      .subscribe(response => {
        if (!response) {
          return;
        }

        const query = this.queryState.getQuery();

        if (response.draw !== query.draw) {
          return;
        }

        const countriesWithSerialNumbers: CountryTableRow[] = response.rows.map((country, index) => ({
          ...country,
          serialNumber: (query.page - 1) * query.pageSize + index + 1
        }));

        this.countries.set(countriesWithSerialNumbers);
        this.totalRecords.set(response.total);
        this.filteredRecords.set(response.filtered);
      });
  }

  ngOnInit(): void {
    this.loadCountries(this.queryState.getQuery());
  }

  loadCountries(query: DataTableQuery): void {
    this.countryQueryRequests$.next(query);
  }

  onSearch(value: string): void {
    const query = this.queryState.setSearch(value.trim());
    this.loadCountries(query);
  }

  onPageChange(event: DataTablePageEvent): void {
    const query = this.queryState.setPage(event);
    this.loadCountries(query);
  }

  onSortChange(event: DataTableSortEvent): void {
    const query = this.queryState.setSort(event);
    this.loadCountries(query);
  }

  onRefresh(): void {
    const currentQuery = this.queryState.getQuery();

    this.loadCountries({
      ...currentQuery,
      draw: currentQuery.draw + 1
    });
  }

  onReset(): void {
    const query = this.queryState.reset();
    this.loadCountries(query);
  }

  onDeleteConfirmed(): void {
    const country = this.pendingDeleteCountry();

    if (!country) {
      return;
    }

    this.pendingDeleteCountry.set(null);
    this.deleteCountry(country);
  }

  onDeleteCancelled(): void {
    this.pendingDeleteCountry.set(null);
  }

  onActionClick(event: DataTableActionEvent<CountryTableRow>): void {
    const country = this.toCountryDto(event.row);

    switch (event.action.type) {
      case 'view':
        this.viewCountry(country);
        break;
      case 'edit':
        this.openEditCountry(country);
        break;
      case 'delete':
        this.requestDeleteCountry(country);
        break;
      case 'activate':
        this.activateCountry(country);
        break;
    }
  }

  onAddCountry(): void {
    this.countryModalMode.set('create');
    this.selectedCountryId.set(null);
    this.selectedCountry.set(null);
    this.selectedCurrency.set(null);
    this.countrySubmitAttempted.set(false);

    this.countryForm.reset({
      name: '',
      iso2: '',
      iso3: '',
      phoneCode: '',
      defaultCurrencyId: null
    });
    this.resetCountryFormState();

    this.showCountryModal.set(true);
  }

  closeCountryModal(): void {
    if (this.saving()) {
      return;
    }

    this.showCountryModal.set(false);
    this.selectedCountryId.set(null);
    this.selectedCountry.set(null);
    this.selectedCurrency.set(null);
    this.countrySubmitAttempted.set(false);
  }

  saveCountry(): void {
    if (this.countryForm.invalid) {
      this.countrySubmitAttempted.set(true);
      this.countryForm.markAllAsTouched();
      this.focusFirstInvalidCountryControl();
      return;
    }

    if (this.saving()) {
      return;
    }

    this.saving.set(true);

    if (this.countryModalMode() === 'create') {
      this.countryApi
        .createCountry(this.buildCreateCountryRequest())
        .pipe(finalize(() => this.saving.set(false)))
        .subscribe({
          next: () => {
            this.toastr.success('Country saved successfully.');
            this.finishCountrySave();
          }
        });

      return;
    }

    const countryId = this.selectedCountryId();

    if (!countryId) {
      this.saving.set(false);
      return;
    }

    this.countryApi
      .updateCountry(
        countryId,
        this.buildUpdateCountryRequest(this.selectedCountry()?.isActive ?? true)
      )
      .pipe(finalize(() => this.saving.set(false)))
      .subscribe({
        next: () => {
          this.toastr.success('Country updated successfully.');
          this.finishCountrySave();
        }
      });
  }

  openEditCountry(country: CountryDto): void {
    this.countryModalMode.set('edit');
    this.selectedCountryId.set(country.id);
    this.selectedCountry.set(null);
    this.selectedCurrency.set(null);
    this.countrySubmitAttempted.set(false);

    this.countryApi
      .getCountryById(country.id)
      .pipe(
        switchMap(countryDetails => {
          const currencyId = countryDetails.defaultCurrencyId;

          if (!currencyId) {
            return of({ countryDetails, currency: null });
          }

          return this.currencyApi.getCurrencyById(currencyId).pipe(
            map(currency => ({ countryDetails, currency })),
            catchError(() => {
              this.toastr.error('Unable to load the selected currency.');
              return of({ countryDetails, currency: null });
            })
          );
        })
      )
      .subscribe({
        next: ({ countryDetails, currency }) => {
          this.selectedCountry.set(countryDetails);
          this.selectedCurrency.set(currency);
          this.countryForm.reset({
            name: countryDetails.name ?? '',
            iso2: countryDetails.iso2 ?? '',
            iso3: countryDetails.iso3 ?? '',
            phoneCode: countryDetails.phoneCode ?? '',
            defaultCurrencyId: countryDetails.defaultCurrencyId ?? null
          });
          this.resetCountryFormState();

          this.showCountryModal.set(true);
        }
      });
  }

  getFlagUrl(iso2: string | null | undefined): string {
    const code = iso2?.trim().toLowerCase();

    return code && /^[a-z]{2}$/.test(code)
      ? `https://flagcdn.com/24x18/${code}.png`
      : '';
  }

  onFlagError(event: Event): void {
    const image = event.target as HTMLImageElement;
    image.style.display = 'none';
  }

  private viewCountry(country: CountryDto): void {
    this.openEditCountry(country);
  }

  private requestDeleteCountry(country: CountryDto): void {
    this.pendingDeleteCountry.set(country);
    this.deleteConfirmDialog()?.open();
  }

  private deleteCountry(country: CountryDto): void {
    this.updateCountryStatus(country, false);
  }

  private activateCountry(country: CountryDto): void {
    this.updateCountryStatus(country, true);
  }

  private buildCreateCountryRequest(): CreateCountryRequest {
    const value = this.countryForm.getRawValue();

    return {
      name: value.name.trim(),
      iso2: value.iso2.trim().toUpperCase(),
      iso3: value.iso3.trim().toUpperCase(),
      phoneCode: this.nullWhenBlank(value.phoneCode),
      defaultCurrencyId: this.nullWhenBlank(value.defaultCurrencyId)
    };
  }

  private buildUpdateCountryRequest(isActive: boolean): UpdateCountryRequest {
    return {
      ...this.buildCreateCountryRequest(),
      isActive
    };
  }

  private countryToUpdateRequest(country: CountryDto, isActive: boolean): UpdateCountryRequest {
    return {
      name: country.name?.trim() ?? '',
      iso2: country.iso2?.trim().toUpperCase() ?? '',
      iso3: country.iso3?.trim().toUpperCase() ?? '',
      phoneCode: this.nullWhenBlank(country.phoneCode),
      defaultCurrencyId: this.nullWhenBlank(country.defaultCurrencyId),
      isActive
    };
  }

  private updateCountryStatus(country: CountryDto, isActive: boolean): void {
    this.countryApi
      .updateCountry(country.id, this.countryToUpdateRequest(country, isActive))
      .subscribe({
        next: () => {
          this.toastr.success(
            isActive
              ? 'Country activated successfully.'
              : 'Country deactivated successfully.'
          );
          this.loadCountries(this.queryState.getQuery());
        }
      });
  }

  private resetCountryFormState(): void {
    this.countryForm.markAsPristine();
    this.countryForm.markAsUntouched();
    this.countryForm.updateValueAndValidity();
  }

  private finishCountrySave(): void {
    this.showCountryModal.set(false);
    this.selectedCountryId.set(null);
    this.selectedCountry.set(null);
    this.countrySubmitAttempted.set(false);
    this.loadCountries(this.queryState.getQuery());
  }

  private clearCountryGrid(): void {
    this.countries.set([]);
    this.totalRecords.set(0);
    this.filteredRecords.set(0);
  }

  private focusFirstInvalidCountryControl(): void {
    queueMicrotask(() => {
      const firstInvalidControl =
        this.elementRef.nativeElement.querySelector<HTMLElement>(
          'modal [data-form-control][aria-invalid="true"]'
        );

      firstInvalidControl?.focus();
      firstInvalidControl?.scrollIntoView({
        behavior: 'smooth',
        block: 'center'
      });
    });
  }

  private nullWhenBlank(value: string | null | undefined): string | null {
    const normalized = value?.trim();

    return normalized
      ? normalized
      : null;
  }

  private toCountryDto(row: CountryTableRow): CountryDto {
    return {
      id: row.id,
      iso2: row.iso2,
      iso3: row.iso3,
      name: row.name,
      phoneCode: row.phoneCode,
      defaultCurrencyId: row.defaultCurrencyId,
      isActive: row.isActive,
      createdOn: row.createdOn,
      modifiedOn: row.modifiedOn
    };
  }
}
