import { and, asc, count, desc, eq, ilike, or } from 'drizzle-orm';
import { courses } from '@/db/schema';
import { db } from '@/lib/db';
import type { CourseFilters, CourseMutationPayload } from '../api/types';

type CourseRow = {
  id: number;
  organizationId: string;
  courseCode: string | null;
  name: string;
  category: string;
  organizer: string | null;
  standardHours: number;
  certificateValidityMonths: number | null;
  isRequired: boolean;
  certificateRequired: boolean;
  trainingCost: number | null;
  description: string | null;
  isActive: boolean;
  createdAt: Date;
  updatedAt: Date;
};

const sortMap = {
  course_code: courses.courseCode,
  name: courses.name,
  category: courses.category,
  standard_hours: courses.standardHours,
  training_cost: courses.trainingCost,
  updated_at: courses.updatedAt
} as const;

function normalizeOptionalText(value?: string | null) {
  const normalized = value?.trim();
  return normalized ? normalized : null;
}

function normalizeSingleFilterValue(value?: string | null) {
  const normalized = value
    ?.split(/[.,]/)
    .map((item) => item.trim())
    .find(Boolean);

  return normalized && normalized !== 'all' ? normalized : null;
}

function toStatusFilter(status?: string | null) {
  const normalizedStatus = normalizeSingleFilterValue(status)?.toLowerCase();

  if (normalizedStatus === 'active') return eq(courses.isActive, true);
  if (normalizedStatus === 'inactive') return eq(courses.isActive, false);

  return null;
}

export function serializeCourse(course: CourseRow) {
  return {
    id: course.id,
    organization_id: course.organizationId,
    course_code: course.courseCode,
    name: course.name,
    category: course.category,
    organizer: course.organizer,
    standard_hours: course.standardHours,
    certificate_validity_months: course.certificateValidityMonths,
    is_required: course.isRequired,
    certificate_required: course.certificateRequired,
    training_cost: course.trainingCost,
    description: course.description,
    is_active: course.isActive,
    created_at: course.createdAt.toISOString(),
    updated_at: course.updatedAt.toISOString()
  };
}

export async function listCoursesForOrganization({
  organizationId,
  filters
}: {
  organizationId: string;
  filters: CourseFilters;
}) {
  const page = Number(filters.page ?? 1);
  const limit = Number(filters.limit ?? 10);
  const search = filters.search?.trim();
  const sort = filters.sort;
  const statusFilter = toStatusFilter(filters.status);
  const category = normalizeSingleFilterValue(filters.category);

  const clauses = [
    eq(courses.organizationId, organizationId),
    ...(category ? [eq(courses.category, category)] : []),
    ...(statusFilter ? [statusFilter] : []),
    ...(search
      ? [
          or(
            ilike(courses.courseCode, `%${search}%`),
            ilike(courses.name, `%${search}%`),
            ilike(courses.category, `%${search}%`),
            ilike(courses.organizer, `%${search}%`),
            ilike(courses.description, `%${search}%`)
          )!
        ]
      : [])
  ];

  const where = clauses.length === 1 ? clauses[0] : and(...clauses);

  let orderByClause = desc(courses.createdAt);

  if (sort) {
    try {
      const sortItems = JSON.parse(sort) as Array<{ id: keyof typeof sortMap; desc: boolean }>;
      const primarySort = sortItems[0];

      if (primarySort?.id && sortMap[primarySort.id]) {
        orderByClause = primarySort.desc
          ? desc(sortMap[primarySort.id])
          : asc(sortMap[primarySort.id]);
      }
    } catch {
      orderByClause = desc(courses.createdAt);
    }
  }

  const offset = (page - 1) * limit;
  const [totalResult] = await db.select({ value: count() }).from(courses).where(where);
  const rows = await db
    .select()
    .from(courses)
    .where(where)
    .orderBy(orderByClause)
    .limit(limit)
    .offset(offset);

  return {
    offset,
    limit,
    total: totalResult?.value ?? 0,
    courses: rows.map((row) => serializeCourse(row))
  };
}

export async function getCourseByIdForOrganization({
  id,
  organizationId
}: {
  id: number;
  organizationId: string;
}) {
  const course = await db.query.courses.findFirst({
    where: and(eq(courses.id, id), eq(courses.organizationId, organizationId))
  });

  return course ?? null;
}

export function normalizeCoursePayload(payload: CourseMutationPayload) {
  return {
    ...payload,
    courseCode: normalizeOptionalText(payload.courseCode),
    organizer: normalizeOptionalText(payload.organizer),
    description: normalizeOptionalText(payload.description),
    certificateValidityMonths:
      payload.certificateValidityMonths === undefined ? null : payload.certificateValidityMonths,
    trainingCost: payload.trainingCost === undefined ? null : payload.trainingCost
  };
}
