import { HttpErrorResponse } from '@angular/common/http';
import { Component, DestroyRef, ElementRef, computed, inject, signal } 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 { TenantCurrenciesService } from '../../data-access/tenant-currencies.service';
import { LanguageLookupDto, LanguageService } from '../../../global-masters/languages/public-api';
import { CurrencyLookupDto, CurrencyService } from '../../../global-masters/currencies/public-api';
import { TimezoneService } from '../../../global-masters/timezones/public-api';
import { DataTable } from '../../../../shared/components/data-table/data-table';
import { DataTableAction, DataTableActionEvent, DataTableColumn, DataTablePageEvent, DataTableQuery, DataTableSortEvent } from '../../../../shared/components/data-table/data-table.types';
import { DataTableQueryState } from '../../../../shared/components/data-table/data-table-query.state';

import { CreateTenantCurrencyRequest, TenantCurrencyDto, TenantCurrencyModalMode, TenantCurrencyTableRow, UpdateTenantCurrencyRequest } from '../../models/tenant-currencies.model';
import { AutocompleteDisplayFn, AutocompleteResolveValueFn, AutocompleteSearchFn, AutocompleteValueFn } from '../../../../shared/components/form/autocomplete/autocomplete.types';
import { TenantLookupDto } from '../../models/tenant.model';
import { TenantService } from '../../data-access/tenant.service';
import { Autocomplete } from '../../../../shared/components/form/autocomplete/autocomplete';
import { FormSelect } from '../../../../shared/components/form/form-select/form-select';
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';

@Component({
  selector: 'tenant-currencies',
  imports: [DataTable, Modal, ReactiveFormsModule, FormInput, FormSelect, Autocomplete, ConfirmDialog, FilterCard],
  templateUrl: './tenant-currencies.html',
  styleUrl: './tenant-currencies.scss',
})
export class TenantCurrencies {

  private readonly destroyRef = inject(DestroyRef);
  private readonly tenantCurrencyApi = inject(TenantCurrenciesService);
  private readonly tenantApi = inject(TenantService);
  private readonly languageApi = inject(LanguageService);
  private readonly currencyApi = inject(CurrencyService);
  private readonly timezoneApi = inject(TimezoneService);
  private readonly formBuilder = inject(FormBuilder);
  private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);
  private readonly toastr = inject(ToastrService);
  private readonly queryRequests$ = new Subject<{ tenantId: string; query: DataTableQuery; }>();

  readonly queryState = new DataTableQueryState();
  readonly tenantCurrencies = signal<TenantCurrencyTableRow[]>([]);
  readonly totalRecords = signal(0);
  readonly filteredRecords = signal(0);

  readonly saving = signal(false);
  readonly showTenantCurrencyModal = signal(false);
  readonly tenantCurrencyModalMode = signal<TenantCurrencyModalMode>('create');
  readonly selectedTenantId = signal<string | null>(null);
  readonly selectedTenantLookup = signal<TenantLookupDto | null>(null);
  readonly tenantSubmitAttempted = signal(false);



  readonly tenantCurrencyForm = this.formBuilder.group({
    tenantId: this.formBuilder.control<string | null>(null, [Validators.required]),
    currencyId: this.formBuilder.control<string | null>(null, [Validators.required]),
    isActive: this.formBuilder.control<number | null>(1, [Validators.required]),
    isBaseCurrency: this.formBuilder.control<boolean>(false, [Validators.required]),
    isReporting: this.formBuilder.control<boolean>(false, [Validators.required]),
  });
  readonly tenantCurrencyFilterForm = this.formBuilder.nonNullable.group({
    tenantId: ['']
  });

  readonly searchTenants:
    AutocompleteSearchFn<TenantLookupDto> =
    (term, limit) =>
      this.tenantApi.autocomplete(term, limit).pipe(
        catchError(() => {
          this.toastr.error(
            'Unable to load tenants.'
          );

          return of<
            readonly TenantLookupDto[]
          >([]);
        })
      );

  readonly displayTenant:
    AutocompleteDisplayFn<TenantLookupDto> =
    tenant =>
      [tenant.code, tenant.name]
        .filter(Boolean)
        .join(' - ');

  readonly tenantValue:
    AutocompleteValueFn<
      TenantLookupDto,
      string
    > =
    tenant => tenant.id;

  readonly resolveTenant:
    AutocompleteResolveValueFn<
      TenantLookupDto,
      string
    > =
    tenantId =>
      this.tenantApi
        .getTenantById(tenantId)
        .pipe(
          map(tenant => ({
            id: tenant.id,
            code: tenant.code,
            name: tenant.name
          }))
        );

  readonly searchCurrencies: AutocompleteSearchFn<CurrencyLookupDto> = (term, limit) =>
    this.currencyApi.autocomplete(term, limit).pipe(
      catchError(() => {
        this.toastr.error('Unable to load currencies.');
        return of<readonly CurrencyLookupDto[]>([]);
      })
    );

  readonly displayCurrency: AutocompleteDisplayFn<CurrencyLookupDto> = currency => [currency.code, currency.name, currency.symbol ? `(${currency.symbol})` : '']
    .filter(Boolean)
    .join(' ');

  readonly currencyValue: AutocompleteValueFn<CurrencyLookupDto, string> = currency => currency.id;

  readonly resolveCurrency: AutocompleteResolveValueFn<CurrencyLookupDto, string> = value =>
    this.currencyApi.getCurrencyById(value).pipe(
      map(currency => ({
        id: currency.id,
        code: currency.code,
        name: currency.name,
        symbol: currency.symbol
      }))
    );


  readonly modalTitle = computed(() =>
    this.tenantCurrencyModalMode() === 'create' ? 'Add Tenant' : 'Edit Tenant'
  );

  readonly submitLabel = computed(() =>
    this.tenantCurrencyModalMode() === 'create' ? 'Save' : 'Update'
  );

  readonly loadingLabel = computed(() =>
    this.tenantCurrencyModalMode() === 'create' ? 'Saving...' : 'Updating...'
  );

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

  readonly columns = signal<DataTableColumn<TenantCurrencyTableRow>[]>([
    { key: 'serialNumber', label: 'Sr. No.', header: 'Sr.No.', sortable: false, width: '100px' },
    { key: 'code', label: 'Code', header: 'Code', sortable: true, align: 'left' },
    { key: 'name', label: 'Tenant Name', header: 'Tenant Name', sortable: true, align: 'left' },
    {
      key: 'isBaseCurrency',
      label: 'Base Currency',
      header: 'Base Currency',
      sortable: true,
      badge: true,
      formatter: value => value === true ? 'Yes' : 'No'
    },
    {
      key: 'isReporting',
      label: 'Reporting Currency',
      header: 'Reporting Currency',
      sortable: true,
      badge: true,
      formatter: value => value === true ? 'Yes' : 'No'
    },
    {
      key: 'isActive',
      label: 'Active',
      header: 'Active',
      sortable: true,
      badge: true,
      badgeClass: value => value === true
        ? 'badge bg-success/10 text-success'
        : 'badge bg-danger/10 text-danger',
      formatter: value => value === true ? 'Active' : 'Inactive'
    },
    {
      key: 'createdOn',
      label: 'Created On',
      header: 'Created On',
      sortable: true,
      formatter: value => this.formatDateTime(value)
    }
  ]);

  readonly actions = signal<DataTableAction<TenantCurrencyTableRow>[]>([
    {
      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
    }
  ]);

  private formatDateTime(value: unknown): string {
    if (typeof value !== 'string' || value.trim().length === 0) {
      return '';
    }

    const date = new Date(value);

    if (Number.isNaN(date.getTime())) {
      return value;
    }

    return date.toLocaleString();
  }

  constructor() {
    this.queryRequests$
      .pipe(
        switchMap(({ tenantId, query }) =>
          this.tenantCurrencyApi
            .getTenantDataTable(this.buildTenantCurrencyQuery(query))
            .pipe(
              map(response => ({
                response,
                tenantId,
                query
              })),
              catchError(() => {
                this.toastr.error(
                  'Unable to load tenant currencies.'
                );

                this.clearTenantCurrencyGrid();

                return of(null);
              })
            )
        ),
        takeUntilDestroyed(this.destroyRef)
      )
      .subscribe(result => {
        if (!result) {
          return;
        }

        const {
          response,
          tenantId,
          query
        } = result;

        if (this.selectedTenantId() !== tenantId) {
          return;
        }

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

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

        this.tenantCurrencies.set(rows);
        this.totalRecords.set(response.total);
        this.filteredRecords.set(
          response.filtered
        );
      });
  }
  ngOnInit(): void {
    this.clearTenantCurrencyGrid();
  }

  loadTenantCurrencies(query: DataTableQuery): void {
    const tenantId = this.selectedTenantId();

    if (!tenantId) {
      this.clearTenantCurrencyGrid();
      return;
    }

    this.queryRequests$.next({
      tenantId,
      query
    });
  }

  onSearch(value: string): void {
    this.loadTenantCurrencies(this.queryState.setSearch(value.trim()));
  }

  onPageChange(event: DataTablePageEvent): void {
    this.loadTenantCurrencies(this.queryState.setPage(event));
  }

  onSortChange(event: DataTableSortEvent): void {
    this.loadTenantCurrencies(this.queryState.setSort(event));
  }

  onActionClick(event: DataTableActionEvent<TenantCurrencyTableRow>): void {
    if (event.action.type === 'edit') {
      this.openEditTenantCurrency(event.row.id);
    }
  }
  private buildTenantCurrencyQuery(query: DataTableQuery): DataTableQuery {
    return {
      ...query,
      sortBy: this.resolveSortField(query.sortBy ?? null)
    };
  }

  private resolveSortField(sortBy: string | null | undefined): string | null {
    if (!sortBy) {
      return null;
    }

    switch (sortBy) {
      case 'code':
      case 'name':
      case 'status':
      case 'dataRegion':
      case 'isActive':
      case 'createdOn':
        return sortBy;
      default:
        return null;
    }
  }

  onTenantLookupSelected(tenant: TenantLookupDto | null): void {
    this.selectedTenantLookup.set(tenant);

    const tenantId = tenant?.id ?? null;

    this.tenantCurrencyFilterForm.controls.tenantId
      .setValue(tenantId ?? '');

    this.onTenantSelected(tenantId);
  }

  onAddTenantCurrency(): void {
    this.tenantCurrencyModalMode.set('create');

    this.showTenantCurrencyModal.set(true);
  }

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

    this.showTenantCurrencyModal.set(false);
  }

  private openEditTenantCurrency(id: string): void {
    this.tenantCurrencyModalMode.set('edit');

  }

  private onTenantSelected(tenantId: string | null): void {
    this.selectedTenantId.set(tenantId);


    this.clearTenantCurrencyGrid();

    const query = this.queryState.reset();

    if (!tenantId) {
      return;
    }

    this.loadTenantCurrencies(query);
  }


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

}
