import type { DB } from "../generated/kysely-tailordb";
import {
  ValidationErrorError,
  InvalidScopeError,
  UnauthorizedError,
} from "../lib/errors.generated";
import {
  ok,
  err,
  requirePermission,
  type ReadonlyDB,
  type QueryContext,
  type PaginationInput,
  buildPaginatedResult,
  DEFAULT_PAGE_SIZE,
} from "@tailor-platform/erp-kit/module";

const VALID_SCOPES = ["COMPANY", "GLOBAL", "ALL"];

type SearchOrderByField = "timestamp" | "entityType" | "operationType" | "actorType";

export interface SearchAuditEntriesInput extends PaginationInput<SearchOrderByField> {
  actorId?: string;
  actorType?: string;
  entityType?: string;
  entityId?: string;
  operationType?: string;
  companyId?: string;
  correlationId?: string;
  dateFrom?: Date;
  dateTo?: Date;
  scope?: string;
}

export async function run(db: ReadonlyDB<DB>, input: SearchAuditEntriesInput, ctx: QueryContext) {
  const permCheck = requirePermission(ctx, "audit:viewAuditHistory");
  if (!permCheck.ok) return permCheck;

  // Validate scope
  if (input.scope && !VALID_SCOPES.includes(input.scope)) {
    return err(new InvalidScopeError(input.scope));
  }
  if (input.scope === "COMPANY" && !input.companyId) {
    return err(new ValidationErrorError("scope=COMPANY requires companyId"));
  }

  // Check at least one filter
  const hasFilter =
    input.actorId !== undefined ||
    input.actorType !== undefined ||
    input.entityType !== undefined ||
    input.entityId !== undefined ||
    input.operationType !== undefined ||
    input.companyId !== undefined ||
    input.correlationId !== undefined ||
    input.dateFrom !== undefined ||
    input.dateTo !== undefined ||
    input.scope !== undefined;
  if (!hasFilter) {
    return err(new ValidationErrorError("At least one filter parameter is required"));
  }

  const limit = input.limit ?? DEFAULT_PAGE_SIZE;
  const offset = input.offset ?? 0;
  const orderBy = input.orderBy ?? "timestamp";
  const orderDirection = input.orderDirection ?? "asc";

  let query = db.selectFrom("AuditEntry").selectAll();

  // Apply filters
  if (input.actorId) query = query.where("actorId", "=", input.actorId);
  if (input.actorType) query = query.where("actorType", "=", input.actorType);
  if (input.entityType) query = query.where("entityType", "=", input.entityType);
  if (input.entityId) query = query.where("entityId", "=", input.entityId);
  if (input.operationType) query = query.where("operationType", "=", input.operationType);
  if (input.correlationId) query = query.where("correlationId", "=", input.correlationId);
  if (input.dateFrom) query = query.where("timestamp", ">=", input.dateFrom);
  if (input.dateTo) query = query.where("timestamp", "<=", input.dateTo);

  // Apply company-scope restriction from context
  if (ctx.companyId !== undefined) {
    // Reject if caller requests a company they are not authorized for
    if (ctx.companyId !== null && input.companyId && input.companyId !== ctx.companyId) {
      return err(new UnauthorizedError(ctx.actorId));
    }
    if (ctx.companyId === null) {
      // Global-scoped caller: cannot see company-scoped entries
      if (input.scope === "COMPANY") {
        return ok(buildPaginatedResult([], limit));
      }
      query = query.where("companyId", "is", null);
    } else {
      // Company-scoped caller: cannot see global entries
      if (input.scope === "GLOBAL") {
        return ok(buildPaginatedResult([], limit));
      }
      query = query.where("companyId", "=", ctx.companyId);
    }
  } else {
    // No scope restriction from context; apply input scope filtering
    if (input.scope === "COMPANY" || (!input.scope && input.companyId)) {
      query = query.where("companyId", "=", input.companyId!);
    } else if (input.scope === "GLOBAL" || (!input.scope && !input.companyId)) {
      query = query.where("companyId", "is", null);
    } else if (input.scope === "ALL" && input.companyId) {
      // scope=ALL with companyId: return combined company + global results
      query = query.where((eb) =>
        eb.or([eb("companyId", "=", input.companyId!), eb("companyId", "is", null)]),
      );
    }
    // scope=ALL without companyId: no companyId filter (return all scopes)
  }

  const entries = await query
    .orderBy(orderBy, orderDirection)
    .limit(limit + 1)
    .offset(offset)
    .execute();

  return ok(buildPaginatedResult(entries, limit));
}
