import i18next from 'i18next';
import { array, type InferType, lazy, number, object, string } from 'yup';

import { type ArrayElement } from 'src/types/array';

import {
  MAX_CATEGORY_TITLE_LENGTH,
  MIN_CATEGORY_TITLE_LENGTH,
} from '../../constants';

const categoryChildBaseSchema = object({
  name: string()
    .trim()
    .required(i18next.t('validations:required'))
    .min(
      MIN_CATEGORY_TITLE_LENGTH,
      i18next.t('validations:min_length', { count: MIN_CATEGORY_TITLE_LENGTH }),
    )
    .max(
      MAX_CATEGORY_TITLE_LENGTH,
      i18next.t('validations:max_length', {
        count: MAX_CATEGORY_TITLE_LENGTH,
      }),
    ),
  id: string(),
  originalId: number().optional(),
  status: string().defined(),
  amountOfCourses: number().defined(),
});

const getDuplicateNames = (children: ManageCategoryChildItem[]) => {
  const seen = new Set<string>();
  const duplicates = new Set<string>();

  for (const child of children) {
    const name = child.name?.trim();
    if (!name) continue;

    if (seen.has(name)) duplicates.add(name);
    else seen.add(name);
  }

  return duplicates;
};

const categoryChildSchema: any = lazy(() =>
  categoryChildBaseSchema
    .test('unique-name', i18next.t('validations:name_in_use'), (child, ctx) => {
      if (!child?.name) return true;
      const children = ctx.parent as ManageCategoryChildItem[];

      if (!Array.isArray(children)) return true;

      const duplicates = getDuplicateNames(children);

      if (!duplicates.has(child.name.trim())) return true;

      return ctx.createError({
        message: i18next.t('validations:name_in_use'),
        path: `${ctx.path}.name`,
      });
    })
    .shape({
      children: array(categoryChildSchema).optional(),
    }),
);

export const manageCategorySchema = object({
  children: array(categoryChildSchema).required(),
});

export type ManageCategoryFormValues = InferType<typeof manageCategorySchema>;

export type ManageCategoryChildItem = ArrayElement<
  ManageCategoryFormValues['children']
>;
