import { and, asc, count, desc, eq, gte, ilike, lt, or, sql } from 'drizzle-orm';
import { permissionAuditLogs, permissionTemplates, users } from '@/db/schema';
import { db } from '@/lib/db';
import type { PermissionAuditLogFilters } from '../api/types';

const sortMap = {
  created_at: permissionAuditLogs.createdAt,
  actor_user_name: users.name,
  action: permissionAuditLogs.action
} as const;

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

function toDate(value: string, endOfDay: boolean) {
  return new Date(`${value}T${endOfDay ? '23:59:59.999' : '00:00:00.000'}Z`);
}

export async function listPermissionAuditLogsForOrganization({
  organizationId,
  filters
}: {
  organizationId: string;
  filters: PermissionAuditLogFilters;
}) {
  const page = Number(filters.page ?? 1);
  const limit = Number(filters.limit ?? 10);
  const search = normalizeText(filters.search);
  const userSearch = normalizeText(filters.user);
  const action = normalizeText(filters.action);
  const dateFrom = normalizeText(filters.dateFrom);
  const dateTo = normalizeText(filters.dateTo);

  const clauses = [
    eq(permissionAuditLogs.organizationId, organizationId),
    ...(action ? [eq(permissionAuditLogs.action, action)] : []),
    ...(userSearch
      ? [ilike(users.name, `%${userSearch}%`)]
      : []),
    ...(dateFrom ? [gte(permissionAuditLogs.createdAt, toDate(dateFrom, false))] : []),
    ...(dateTo ? [lt(permissionAuditLogs.createdAt, new Date(toDate(dateTo, true).getTime() + 1))] : []),
    ...(search
      ? [
          or(
            ilike(users.name, `%${search}%`),
            ilike(permissionAuditLogs.action, `%${search}%`),
            ilike(permissionTemplates.name, `%${search}%`)
          )!
        ]
      : [])
  ];

  const where = clauses.length > 1 ? and(...clauses) : clauses[0];
  let orderByClause = desc(permissionAuditLogs.createdAt);

  if (filters.sort) {
    try {
      const sortItems = JSON.parse(filters.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(permissionAuditLogs.createdAt);
    }
  }

  const baseQuery = db
    .select({
      id: permissionAuditLogs.id,
      actorUserName: users.name,
      targetUserName: sql<string | null>`null`,
      templateName: permissionTemplates.name,
      action: permissionAuditLogs.action,
      reason: permissionAuditLogs.reason,
      before: permissionAuditLogs.before,
      after: permissionAuditLogs.after,
      createdAt: permissionAuditLogs.createdAt
    })
    .from(permissionAuditLogs)
    .leftJoin(users, eq(permissionAuditLogs.actorUserId, users.id))
    .leftJoin(permissionTemplates, eq(permissionAuditLogs.templateId, permissionTemplates.id))
    .where(where);

  const countSubquery = baseQuery.as('scoped_permission_audit_logs');
  const [totalResult] = await db.select({ value: count() }).from(countSubquery);
  const offset = (page - 1) * limit;
  const rows = await baseQuery.orderBy(orderByClause).limit(limit).offset(offset);

  return {
    offset,
    limit,
    total: totalResult?.value ?? 0,
    permissionAuditLogs: rows.map((row) => ({
      id: row.id,
      actor_user_name: row.actorUserName,
      target_user_name: row.targetUserName,
      template_name: row.templateName,
      action: row.action,
      reason: row.reason,
      before: row.before as Record<string, unknown> | null,
      after: row.after as Record<string, unknown> | null,
      created_at: row.createdAt.toISOString()
    }))
  };
}
