import { inject, InjectionToken, Pipe, type PipeTransform } from '@angular/core';
import { type ValidationErrors } from '@angular/forms';
import { TranslocoService } from '@jsverse/transloco';

/**
 * Maps a validator key to a transloco key and an optional params extractor.
 * Keys are resolved within the nested `validations` object of the global scope.
 *
 * @example
 * ```ts
 * const rules: ValidationMessageRules = {
 *   required: { key: 'Validations.Required' },
 *   maxlength: {
 *     key: 'Validations.MaxLength',
 *     params: (err) => ({ max: err.requiredLength }),
 *   },
 * };
 * ```
 */
export interface ValidationMessageRule {
  key: string;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  params?: (error: any) => Record<string, unknown>;
}

export type ValidationMessageRules = Record<string, ValidationMessageRule>;

/**
 * Default rules for built-in Angular validators.
 * Override or extend via `provideValidationMessages()`.
 */
export const DEFAULT_VALIDATION_RULES: ValidationMessageRules = {
  required: { key: 'Validations.Required' },
  minlength: {
    key: 'Validations.MinLength',
    params: (err: { requiredLength: number }) => ({ min: err.requiredLength }),
  },
  maxlength: {
    key: 'Validations.MaxLength',
    params: (err: { requiredLength: number }) => ({ max: err.requiredLength }),
  },
  min: {
    key: 'Validations.Min',
    params: (err: { min: number }) => ({ min: err.min }),
  },
  max: {
    key: 'Validations.Max',
    params: (err: { max: number }) => ({ max: err.max }),
  },
  email: { key: 'Validations.Email' },
  pattern: { key: 'Validations.Pattern' },
};

export const VALIDATION_MESSAGE_RULES = new InjectionToken<ValidationMessageRules>(
  'VALIDATION_MESSAGE_RULES',
  { providedIn: 'root', factory: () => DEFAULT_VALIDATION_RULES },
);

/**
 * Provide custom validation message rules, merged with defaults.
 *
 * @example
 * ```ts
 * // app.config.ts
 * provideValidationMessages({
 *   uniqueEmail: { key: 'Validations.UniqueEmail' },
 *   compare: { key: 'Validations.Compare' },
 * })
 * ```
 */
export function provideValidationMessages(rules: ValidationMessageRules) {
  return {
    provide: VALIDATION_MESSAGE_RULES,
    useValue: { ...DEFAULT_VALIDATION_RULES, ...rules },
  };
}

/**
 * Impure pipe that resolves the first validation error to a translated message.
 * Uses the `validations` nested key in the global transloco scope.
 *
 * @example
 * ```html
 * <hlm-field-error>
 *   {{ signalForm.get('name')?.errors | validationMessage }}
 * </hlm-field-error>
 * ```
 */
@Pipe({ name: 'validationMessage', pure: false })
export class ValidationMessagePipe implements PipeTransform {
  private readonly transloco = inject(TranslocoService);
  private readonly rules = inject(VALIDATION_MESSAGE_RULES);

  private lastErrors: ValidationErrors | null = null;
  private lastLang = '';
  private lastMessage = '';

  transform(errors: ValidationErrors | null | undefined): string {
    if (!errors) {
      this.lastMessage = '';
      this.lastErrors = null;
      return '';
    }

    const lang = this.transloco.getActiveLang();
    if (errors === this.lastErrors && lang === this.lastLang) return this.lastMessage;
    this.lastErrors = errors;
    this.lastLang = lang;

    const firstKey = Object.keys(errors)[0];
    const rule = this.rules[firstKey];

    if (rule) {
      const params = rule.params ? rule.params(errors[firstKey]) : {};
      this.lastMessage = this.transloco.translate(rule.key, params);
    } else {
      this.lastMessage = this.transloco.translate('Validations.Default');
    }

    return this.lastMessage;
  }
}
