import { ChangeDetectionStrategy, Component, ElementRef, inject, signal } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { ToastrService } from 'ngx-toastr';
import { catchError, map, of } from 'rxjs';

import {
  CountryLookupDto,
} from '../../../../global-masters/countries/public-api';
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 {
  OnboardingLookupValue,
  OnboardingStepForm,
  OrganizationBasicsValue,
} from '../../models/organization-onboarding.model';
import { OrganizationOnboardingService } from '../../services/organization-onboarding.service';

interface OrganizationBasicsFormModel {
  readonly organizationName: string;
  readonly shortName: string;
  readonly localLanguageName: string;
  readonly organizationTypeId: string | null;
  readonly industryId: string | null;
  readonly registrationCountryId: string | null;
}

@Component({
  selector: 'app-organization-basics-step',
  standalone: true,
  imports: [ReactiveFormsModule, FormInput, Autocomplete],
  templateUrl: './organization-basics.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class OrganizationBasicsStepComponent implements OnboardingStepForm<OrganizationBasicsValue> {
  private readonly formBuilder = inject(FormBuilder);
  private readonly onboardingService = inject(OrganizationOnboardingService);
  private readonly toastr = inject(ToastrService);
  private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);

  readonly submitAttempted = signal(false);
  readonly organizationCodeControl = this.formBuilder.nonNullable.control('Pending generation');
  readonly organizationTypeSelection = signal<OnboardingLookupValue | null>(null);
  readonly industrySelection = signal<OnboardingLookupValue | null>(null);
  readonly registrationCountrySelection = signal<CountryLookupDto | null>(null);

  readonly form = this.formBuilder.group({
    organizationName: this.formBuilder.nonNullable.control('', [Validators.required, Validators.maxLength(150)]),
    shortName: this.formBuilder.nonNullable.control('', [Validators.required, Validators.maxLength(50)]),
    localLanguageName: this.formBuilder.nonNullable.control('', [Validators.maxLength(150)]),
    organizationTypeId: this.formBuilder.control<string | null>(null, [Validators.required]),
    industryId: this.formBuilder.control<string | null>(null, [Validators.required]),
    registrationCountryId: this.formBuilder.control<string | null>(null, [Validators.required]),
  });

  readonly searchOrganizationTypes: AutocompleteSearchFn<OnboardingLookupValue> = (term, limit) =>
    this.onboardingService.searchOrganizationTypes(term, limit).pipe(
      catchError(() => {
        this.toastr.error('Unable to load organization types.');
        return of<readonly OnboardingLookupValue[]>([]);
      })
    );

  readonly searchIndustries: AutocompleteSearchFn<OnboardingLookupValue> = (term, limit) =>
    this.onboardingService.searchIndustries(term, limit).pipe(
      catchError(() => {
        this.toastr.error('Unable to load industries.');
        return of<readonly OnboardingLookupValue[]>([]);
      })
    );

  readonly searchCountries: AutocompleteSearchFn<CountryLookupDto> = (term, limit) =>
    this.onboardingService.searchRegistrationCountries(term, limit).pipe(
      catchError(() => {
        this.toastr.error('Unable to load countries.');
        return of<readonly CountryLookupDto[]>([]);
      })
    );

  readonly displayLookup: AutocompleteDisplayFn<OnboardingLookupValue> = item =>
    [item.label, item.secondaryLabel].filter(Boolean).join(' - ');

  readonly lookupValue: AutocompleteValueFn<OnboardingLookupValue, string> = item => item.id;

  readonly resolveOrganizationType: AutocompleteResolveValueFn<OnboardingLookupValue, string> = value =>
    this.onboardingService.resolveOrganizationType(value);

  readonly resolveIndustry: AutocompleteResolveValueFn<OnboardingLookupValue, string> = value =>
    this.onboardingService.resolveIndustry(value);

  readonly displayCountry: AutocompleteDisplayFn<CountryLookupDto> = country => country.name;

  readonly countryValue: AutocompleteValueFn<CountryLookupDto, string> = country => country.id;

  readonly resolveCountry: AutocompleteResolveValueFn<CountryLookupDto, string> = value =>
    this.onboardingService.resolveCountry(value);

  validate(): boolean {
    this.submitAttempted.set(true);

    if (this.form.valid) {
      return true;
    }

    this.form.markAllAsTouched();
    this.focusFirstInvalidControl();
    return false;
  }

  getValue(): OrganizationBasicsValue {
    const value = this.form.getRawValue() as OrganizationBasicsFormModel;
    const organizationType = this.organizationTypeSelection();
    const industry = this.industrySelection();
    const registrationCountry = this.registrationCountrySelection();

    if (!organizationType || !industry || !registrationCountry) {
      throw new Error('Organization basics selections are incomplete.');
    }

    return {
      organizationCode: null,
      organizationName: value.organizationName.trim(),
      shortName: value.shortName.trim(),
      localLanguageName: value.localLanguageName.trim() || null,
      organizationType,
      industry,
      registrationCountry: {
        id: registrationCountry.id,
        label: registrationCountry.name,
        iso2: registrationCountry.iso2,
      },
    };
  }

  getDraftValue(): Partial<OrganizationBasicsValue> {
    const value = this.form.getRawValue() as OrganizationBasicsFormModel;

    return {
      organizationCode: null,
      organizationName: value.organizationName,
      shortName: value.shortName,
      localLanguageName: value.localLanguageName || null,
      organizationType: this.organizationTypeSelection() ?? undefined,
      industry: this.industrySelection() ?? undefined,
      registrationCountry: this.registrationCountrySelection()
        ? {
            id: this.registrationCountrySelection()!.id,
            label: this.registrationCountrySelection()!.name,
            iso2: this.registrationCountrySelection()!.iso2,
          }
        : undefined,
    };
  }

  patchValue(value: Partial<OrganizationBasicsValue>): void {
    this.form.patchValue({
      organizationName: value.organizationName ?? '',
      shortName: value.shortName ?? '',
      localLanguageName: value.localLanguageName ?? '',
      organizationTypeId: value.organizationType?.id ?? null,
      industryId: value.industry?.id ?? null,
      registrationCountryId: value.registrationCountry?.id ?? null,
    }, { emitEvent: false });

    this.organizationTypeSelection.set(value.organizationType ?? null);
    this.industrySelection.set(value.industry ?? null);
    this.registrationCountrySelection.set(
      value.registrationCountry
        ? {
            id: value.registrationCountry.id,
            iso2: value.registrationCountry.iso2 ?? '',
            name: value.registrationCountry.label,
          }
        : null
    );
    this.submitAttempted.set(false);
  }

  onOrganizationTypeSelected(value: OnboardingLookupValue): void {
    this.organizationTypeSelection.set(value);
  }

  onOrganizationTypeCleared(): void {
    this.organizationTypeSelection.set(null);
  }

  onIndustrySelected(value: OnboardingLookupValue): void {
    this.industrySelection.set(value);
  }

  onIndustryCleared(): void {
    this.industrySelection.set(null);
  }

  onRegistrationCountrySelected(value: CountryLookupDto): void {
    this.registrationCountrySelection.set(value);
  }

  onRegistrationCountryCleared(): void {
    this.registrationCountrySelection.set(null);
  }

  private focusFirstInvalidControl(): void {
    queueMicrotask(() => {
      const invalidElement = this.elementRef.nativeElement.querySelector<HTMLElement>(
        '.ng-invalid [data-form-control], [data-form-control].ng-invalid, .ng-invalid .ng-select-container'
      );

      invalidElement?.focus();
    });
  }
}