import { type ClassValue, clsx } from 'clsx';
import Isikukood, { Gender } from 'isikukood';
import { twMerge } from 'tailwind-merge';

import type { BmiThresholds } from '@kit/accounts/types/accounts';

import { BmiCategory } from './types/bmi';

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

export function toTitleCase(str?: string) {
  return (
    str
      ?.toLowerCase()
      .replace(/[^-'’\s]+/g, (match) =>
        match.replace(/^./, (first) => first.toUpperCase()),
      ) ?? ''
  );
}

export function sortByDate<T>(
  a: T[] | undefined,
  key: keyof T,
): T[] | undefined {
  return a?.sort((a, b) => {
    if (!a[key] || !b[key]) {
      return 0;
    }
    return (
      new Date(b[key] as string).getTime() -
      new Date(a[key] as string).getTime()
    );
  });
}

export const bmiFromMetric = (kg: number, cm: number) => {
  const m = cm / 100;
  const m2 = m * m;
  if (m2 === 0) {
    return null;
  }
  const bmi = kg / m2;
  return !Number.isNaN(bmi) ? Math.round(bmi) : null;
};

export function getBmiStatus(
  thresholds: Omit<BmiThresholds, 'id'>[],
  params: { age: number; height: number; weight: number },
): BmiCategory | null {
  const age = params.age;
  const thresholdByAge =
    thresholds.find(
      (b) => age >= b.age_min && (b.age_max == null || age <= b.age_max),
    ) || null;
  const bmi = bmiFromMetric(params.weight, params.height);

  if (!thresholdByAge || bmi === null) {
    return null;
  }

  if (bmi > thresholdByAge.obesity_min) return BmiCategory.OBESE;
  if (bmi > thresholdByAge.strong_min) return BmiCategory.VERY_OVERWEIGHT;
  if (bmi > thresholdByAge.overweight_min) return BmiCategory.OVER_WEIGHT;
  if (bmi >= thresholdByAge.normal_min && bmi <= thresholdByAge.normal_max)
    return BmiCategory.NORMAL;
  return BmiCategory.UNDER_WEIGHT;
}

export function getBmiBackgroundColor(bmiStatus: BmiCategory | null): string {
  switch (bmiStatus) {
    case BmiCategory.UNDER_WEIGHT:
    case BmiCategory.OVER_WEIGHT:
      return 'bg-warning';
    case BmiCategory.NORMAL:
      return 'bg-success';
    case BmiCategory.VERY_OVERWEIGHT:
    case BmiCategory.OBESE:
      return 'bg-destructive';
    default:
      return 'bg-success';
  }
}

type AgeRange = '18-29' | '30-39' | '40-49' | '50-59' | '60';
export default class PersonalCode {
  static getPersonalCode(personalCode: string | null) {
    if (!personalCode) {
      return null;
    }
    if (personalCode.toLowerCase().startsWith('ee')) {
      return personalCode.substring(2);
    }
    return personalCode;
  }

  static parsePersonalCode(personalCode: string): {
    ageRange: AgeRange;
    gender: { label: string; value: string };
    dob: Date;
    age: number;
  } {
    const parsed = new Isikukood(personalCode);
    const ageRange = (() => {
      const age = parsed.getAge();
      if (age >= 18 && age <= 29) {
        return '18-29';
      }
      if (age >= 30 && age <= 39) {
        return '30-39';
      }
      if (age >= 40 && age <= 49) {
        return '40-49';
      }
      if (age >= 50 && age <= 59) {
        return '50-59';
      }
      if (age >= 60) {
        return '60';
      }
      throw new Error('Age range not supported, age=' + age);
    })();
    const gender = (() => {
      const gender = parsed.getGender();
      switch (gender) {
        case Gender.FEMALE:
          return { label: 'common:female', value: 'F' };
        case Gender.MALE:
          return { label: 'common:male', value: 'M' };
        default:
          throw new Error('Gender not supported');
      }
    })();

    return {
      ageRange,
      gender,
      dob: parsed.getBirthday(),
      age: parsed.getAge(),
    };
  }
}

export const findProductTypeIdByHandle = (
  productTypes: { metadata?: Record<string, unknown> | null; id: string }[],
  handle: string,
) => {
  return productTypes.find(({ metadata }) => metadata?.handle === handle)?.id;
};
