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, distinctUntilChanged, finalize, map, of, switchMap } from 'rxjs';

import {
  CountryLookupDto
} from '../../../countries/public-api';
import {
  CreateStateRequest,
  StateDto,
  StateModalMode,
  UpdateStateRequest
} from '../../models/state.model';
import { CountryService } from '../../../countries/public-api';
import { StateService } from '../../data-access/state.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 { Autocomplete } from '../../../../../shared/components/form/autocomplete/autocomplete';
import {
  AutocompleteDisplayFn,
  AutocompleteResolveValueFn,
  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';
import { FilterCard } from '../../../../../shared/components/filter-card/filter-card';

interface StateTableRow extends DataTableRecord {
  id: string;
  countryId: string;
  name: string;
  code: string | null;
  isActive: boolean;
  serialNumber: number;
  createdOn?: string;
  modifiedOn?: string | null;
}

@Component({
  selector: 'state-list',
  standalone: true,
  imports: [DataTable, Modal, ReactiveFormsModule, FormInput, Autocomplete, ConfirmDialog, FilterCard],
  templateUrl: './state-list.html',
  styleUrl: './state-list.scss',
})
export class StateList {

  private readonly destroyRef = inject(DestroyRef);
  private readonly stateApi = inject(StateService);
  private readonly countryApi = inject(CountryService);
  private readonly formBuilder = inject(FormBuilder);
  private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);
  private readonly toastr = inject(ToastrService);
  private readonly stateQueryRequests$ = new Subject<DataTableQuery>();

  readonly queryState = new DataTableQueryState();

  readonly states = signal<StateTableRow[]>([]);
  readonly selectedCountryLookup = signal<CountryLookupDto | null>(null);
  readonly selectedFormCountry = signal<CountryLookupDto | null>(null);
  readonly selectedCountryId = signal<string | null>(null);
  readonly appliedCountryId = signal<string | null>(null);
  readonly totalRecords = signal(0);
  readonly filteredRecords = signal(0);
  readonly saving = signal(false);

  readonly showStateModal = signal(false);
  readonly stateModalMode = signal<StateModalMode>('create');
  readonly selectedStateId = signal<string | null>(null);
  readonly selectedState = signal<StateDto | null>(null);
  readonly stateSubmitAttempted = signal(false);
  readonly pendingDeleteState = signal<StateDto | null>(null);
  readonly deleteConfirmDialog = viewChild(ConfirmDialog);

  readonly countryFilterForm = this.formBuilder.nonNullable.group({
    countryId: ['']
  });

  readonly searchCountries: AutocompleteSearchFn<CountryLookupDto> =
    (term, limit) => this.countryApi.autocomplete(term, limit);
  readonly displayCountry: AutocompleteDisplayFn<CountryLookupDto> = country => country.name;
  readonly countryValue: AutocompleteValueFn<CountryLookupDto, string> = country => country.id;
  readonly resolveCountry: AutocompleteResolveValueFn<CountryLookupDto, string> =
    value => this.countryApi.getCountryById(value).pipe(
      map(country => ({ id: country.id, iso2: country.iso2, name: country.name }))
    );

  readonly stateForm = this.formBuilder.nonNullable.group({
    countryId: [
      '',
      [
        Validators.required
      ]
    ],
    name: [
      '',
      [
        Validators.required,
        Validators.maxLength(150)
      ]
    ],
    code: [
      '',
      [
        Validators.required,
        Validators.maxLength(16),
        Validators.pattern(/^[A-Za-z0-9_-]+$/)
      ]
    ]
  });

  readonly emptyMessage = computed(() =>
    this.appliedCountryId()
      ? 'No states found'
      : 'No records found'
  );

  readonly emptyDescription = computed(() =>
    this.appliedCountryId()
      ? 'There are no states available for the selected country.'
      : 'There is currently no data to display.'
  );

  readonly stateModalTitle = computed(() =>
    this.stateModalMode() === 'create'
      ? 'Add State'
      : 'Edit State'
  );

  readonly stateSubmitLabel = computed(() =>
    this.stateModalMode() === 'create'
      ? 'Save'
      : 'Update'
  );

  readonly stateLoadingLabel = computed(() =>
    this.stateModalMode() === 'create'
      ? 'Saving...'
      : 'Updating...'
  );

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

  readonly columns = signal<DataTableColumn<StateTableRow>[]>([
    { key: 'serialNumber', label: 'Sr. No.', header: 'Sr.No.', sortable: false, width: '60px' },
    { key: 'name', header: 'Name', label: 'Name', sortable: true, align: 'left' },
    { key: 'code', header: 'Code', label: 'Code', sortable: true },
    {
      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',
      width: '100px',
      formatter: value => value ? 'Active' : 'Inactive'
    }
  ]);

  readonly actions = signal<DataTableAction<StateTableRow>[]>([
    {
      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.countryFilterForm.controls.countryId.valueChanges
      .pipe(
        distinctUntilChanged(),
        takeUntilDestroyed(this.destroyRef)
      )
      .subscribe(countryId => {
        this.onCountrySelected(countryId || null);
      });

    this.stateQueryRequests$
      .pipe(
        switchMap(query => {
          const countryId = this.appliedCountryId();

          return this.stateApi.getStateDataTable(query, countryId).pipe(
            catchError(() => {
              this.toastr.error('Unable to load states.');
              this.clearStateGrid();
              return of(null);
            })
          );
        }),
        takeUntilDestroyed(this.destroyRef)
      )
      .subscribe(response => {
        if (!response) {
          return;
        }

        const query = this.queryState.getQuery();

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

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

        this.states.set(statesWithSerialNumbers);
        this.totalRecords.set(response.total);
        this.filteredRecords.set(response.filtered);
      });
  }

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

  loadStates(query: DataTableQuery): void {
    this.stateQueryRequests$.next(query);
  }

  onCountryLookupSelected(country: CountryLookupDto): void {
    this.selectedCountryLookup.set(country);
  }

  applyCountryFilter(): void {
    const countryId = this.countryFilterForm.controls.countryId.value || null;
    this.appliedCountryId.set(countryId);

    this.loadStates(this.queryState.setPage({
      pageIndex: 1,
      pageSize: this.queryState.pageSize()
    }));
  }

  onFormCountrySelected(country: CountryLookupDto): void {
    this.selectedFormCountry.set(country);
  }

  onFormCountryCleared(): void {
    this.selectedFormCountry.set(null);
  }

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

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

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

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

    const query: DataTableQuery = {
      ...currentQuery,
      draw: currentQuery.draw + 1
    };

    this.loadStates(query);
  }

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

  onDeleteConfirmed(): void {
    const state = this.pendingDeleteState();

    if (!state) {
      return;
    }

    this.pendingDeleteState.set(null);
    this.deleteState(state);
  }

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

  onActionClick(event: DataTableActionEvent<StateTableRow>): void {
    const action = event.action.type;
    const state = this.toStateDto(event.row);

    switch (action) {
      case 'view':
        this.viewState(state);
        break;
      case 'edit':
        this.openEditState(state);
        break;
      case 'delete':
        this.requestDeleteState(state);
        break;
      case 'activate':
        this.activateState(state);
        break;
    }
  }

  onAddState(): void {
    this.stateModalMode.set('create');
    this.selectedStateId.set(null);
    this.selectedState.set(null);
    this.stateSubmitAttempted.set(false);

    this.selectedFormCountry.set(null);
    this.stateForm.reset({
      countryId: '',
      name: '',
      code: ''
    });
    this.resetStateFormState();

    this.showStateModal.set(true);
  }

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

    this.showStateModal.set(false);
    this.selectedStateId.set(null);
    this.selectedState.set(null);
    this.selectedFormCountry.set(null);
    this.stateSubmitAttempted.set(false);
  }

  saveState(): void {
    if (this.stateForm.invalid) {
      this.stateSubmitAttempted.set(true);
      this.stateForm.markAllAsTouched();
      this.focusFirstInvalidStateControl();
      return;
    }

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

    this.saving.set(true);

    if (this.stateModalMode() === 'create') {
      this.stateApi
        .createState(this.buildCreateStateRequest())
        .pipe(finalize(() => this.saving.set(false)))
        .subscribe({
          next: () => {
            this.toastr.success('State saved successfully.');
            this.finishStateSave();
          }
        });

      return;
    }

    const stateId = this.selectedStateId();

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

    this.stateApi
      .updateState(
        stateId,
        this.buildUpdateStateRequest(this.selectedState()?.isActive ?? true)
      )
      .pipe(finalize(() => this.saving.set(false)))
      .subscribe({
        next: () => {
          this.toastr.success('State updated successfully.');
          this.finishStateSave();
        }
      });
  }

  private onCountrySelected(countryId: string | null): void {
    this.selectedCountryId.set(countryId);

    if (!countryId || this.selectedCountryLookup()?.id !== countryId) {
      this.selectedCountryLookup.set(null);
    }

  }

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

  private viewState(state: StateDto): void {
    this.openEditState(state);
  }

  private openEditState(state: StateDto): void {
    this.stateModalMode.set('edit');
    this.selectedStateId.set(state.id);
    this.selectedState.set(state);
    this.stateSubmitAttempted.set(false);

    this.stateApi
      .getStateById(state.id)
      .subscribe({
        next: stateDetails => {
          this.selectedState.set(stateDetails);
          this.selectedFormCountry.set(null);
          this.stateForm.reset({
            countryId: stateDetails.countryId ?? '',
            name: stateDetails.name ?? '',
            code: stateDetails.code ?? ''
          });
          this.resetStateFormState();

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

  private requestDeleteState(state: StateDto): void {
    this.pendingDeleteState.set(state);
    this.deleteConfirmDialog()?.open();
  }

  private deleteState(state: StateDto): void {
    this.updateStateStatus(state, false);
  }

  private activateState(state: StateDto): void {
    this.updateStateStatus(state, true);
  }

  private buildCreateStateRequest(): CreateStateRequest {
    const value = this.stateForm.getRawValue();

    return {
      countryId: value.countryId,
      name: value.name.trim(),
      code: value.code.trim().toUpperCase()
    };
  }

  private buildUpdateStateRequest(isActive: boolean): UpdateStateRequest {
    const value = this.stateForm.getRawValue();

    return {
      countryId: null,
      name: value.name.trim(),
      code: value.code.trim().toUpperCase(),
      isActive
    };
  }

  private stateToUpdateRequest(state: StateDto, isActive: boolean): UpdateStateRequest {
    return {
      countryId: null,
      name: state.name?.trim() ?? '',
      code: state.code?.trim().toUpperCase() ?? '',
      isActive
    };
  }

  private updateStateStatus(state: StateDto, isActive: boolean): void {
    this.stateApi
      .updateState(state.id, this.stateToUpdateRequest(state, isActive))
      .subscribe({
        next: () => {
          this.toastr.success(
            isActive
              ? 'State activated successfully.'
              : 'State deactivated successfully.'
          );
          this.loadStates(this.queryState.getQuery());
        }
      });
  }

  private resetStateFormState(): void {
    this.stateForm.markAsPristine();
    this.stateForm.markAsUntouched();
    this.stateForm.updateValueAndValidity();
  }

  private finishStateSave(): void {
    this.showStateModal.set(false);
    this.selectedStateId.set(null);
    this.selectedState.set(null);
    this.selectedFormCountry.set(null);
    this.stateSubmitAttempted.set(false);
    this.loadStates(this.queryState.getQuery());
  }

  private focusFirstInvalidStateControl(): 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 toStateDto(row: StateTableRow): StateDto {
    return {
      id: row.id,
      countryId: row.countryId,
      name: row.name,
      code: row.code,
      isActive: row.isActive,
      createdOn: row.createdOn,
      modifiedOn: row.modifiedOn
    };
  }
}
