import { and, asc, desc, eq, ilike, isNull } from 'drizzle-orm';
import { organizations } from '@/db/schema';
import {
  AUDIT_ACTION,
  AUDIT_MODULE,
  logAuditEvent,
  toAuditValue
} from '@/features/audit-logs/server/audit-service';
import { db } from '@/lib/db';
import { seedOrganizationMasterData } from '@/lib/master-data';
import type { OrganizerFilters, OrganizerMutationPayload } from '../api/types';

function slugifyWorkspaceName(value: string) {
  return value
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '');
}

export function serializeOrganizer(row: typeof organizations.$inferSelect) {
  return {
    id: row.id,
    name: row.name,
    slug: row.slug,
    plan: row.plan,
    is_active: row.isActive,
    deleted_at: row.deletedAt ? row.deletedAt.toISOString() : null,
    deleted_by: row.deletedBy,
    created_by: row.createdBy,
    created_at: row.createdAt.toISOString(),
    updated_at: row.updatedAt.toISOString()
  };
}

export async function listOrganizers(filters: OrganizerFilters = {}) {
  const clauses = [
    ...(filters.status === 'active'
      ? [eq(organizations.isActive, true), isNull(organizations.deletedAt)]
      : []),
    ...(filters.status === 'inactive' ? [eq(organizations.isActive, false)] : [])
  ];

  const where = clauses.length > 1 ? and(...clauses) : clauses[0];
  const rows = where
    ? await db
        .select()
        .from(organizations)
        .where(where)
        .orderBy(desc(organizations.isActive), asc(organizations.name))
    : await db
        .select()
        .from(organizations)
        .orderBy(desc(organizations.isActive), asc(organizations.name));

  return rows.map((row) => serializeOrganizer(row));
}

async function findActiveOrganizerByName(name: string, excludeId?: string) {
  const rows = await db
    .select()
    .from(organizations)
    .where(
      and(
        eq(organizations.isActive, true),
        isNull(organizations.deletedAt),
        ilike(organizations.name, name)
      )
    )
    .orderBy(asc(organizations.createdAt));

  return rows.find((row) => row.id !== excludeId) ?? null;
}

export async function createOrganizerRecord({
  actorUserId,
  payload
}: {
  actorUserId: string;
  payload: OrganizerMutationPayload;
}) {
  const name = payload.name.trim();
  const duplicate = await findActiveOrganizerByName(name);

  if (duplicate) {
    throw new Error('DUPLICATE_NAME');
  }

  const organizationId = crypto.randomUUID();
  const baseSlug = slugifyWorkspaceName(name);
  const slug = `${baseSlug || 'organizer'}-${organizationId.slice(0, 6)}`;
  const now = new Date();

  await db.insert(organizations).values({
    id: organizationId,
    name,
    slug,
    plan: 'free',
    isActive: true,
    createdBy: actorUserId,
    createdAt: now,
    updatedAt: now
  });

  await seedOrganizationMasterData(organizationId);

  await logAuditEvent({
    organizationId,
    userId: actorUserId,
    module: AUDIT_MODULE.ORGANIZATIONS,
    action: AUDIT_ACTION.ORGANIZATION_CREATE,
    entityName: 'organization',
    entityId: organizationId,
    newValue: toAuditValue({ id: organizationId, name, slug, isActive: true })
  });

  return organizationId;
}

export async function updateOrganizerRecord({
  organizationId,
  actorUserId,
  payload
}: {
  organizationId: string;
  actorUserId: string;
  payload: OrganizerMutationPayload;
}) {
  const current = await db.query.organizations.findFirst({
    where: eq(organizations.id, organizationId)
  });

  if (!current) {
    throw new Error('NOT_FOUND');
  }

  if (!current.isActive || current.deletedAt) {
    throw new Error('INACTIVE_ORGANIZER');
  }

  const name = payload.name.trim();
  const duplicate = await findActiveOrganizerByName(name, organizationId);

  if (duplicate) {
    throw new Error('DUPLICATE_NAME');
  }

  const before = serializeOrganizer(current);

  await db
    .update(organizations)
    .set({
      name,
      updatedAt: new Date()
    })
    .where(eq(organizations.id, organizationId));

  await logAuditEvent({
    organizationId,
    userId: actorUserId,
    module: AUDIT_MODULE.ORGANIZATIONS,
    action: AUDIT_ACTION.ORGANIZATION_UPDATE,
    entityName: 'organization',
    entityId: organizationId,
    oldValue: toAuditValue(before),
    newValue: toAuditValue({ ...before, name })
  });
}

export async function softDeleteOrganizerRecord({
  organizationId,
  actorUserId
}: {
  organizationId: string;
  actorUserId: string;
}) {
  const current = await db.query.organizations.findFirst({
    where: eq(organizations.id, organizationId)
  });

  if (!current) {
    throw new Error('NOT_FOUND');
  }

  if (!current.isActive || current.deletedAt) {
    throw new Error('INACTIVE_ORGANIZER');
  }

  const before = serializeOrganizer(current);
  const now = new Date();

  await db
    .update(organizations)
    .set({
      isActive: false,
      deletedAt: now,
      deletedBy: actorUserId,
      updatedAt: now
    })
    .where(eq(organizations.id, organizationId));

  await logAuditEvent({
    organizationId,
    userId: actorUserId,
    module: AUDIT_MODULE.ORGANIZATIONS,
    action: AUDIT_ACTION.ORGANIZATION_SOFT_DELETE,
    entityName: 'organization',
    entityId: organizationId,
    oldValue: toAuditValue(before),
    newValue: toAuditValue({
      ...before,
      is_active: false,
      deleted_at: now.toISOString(),
      deleted_by: actorUserId
    })
  });
}
