import { DatabaseError } from "@/lib/error";
import { logError } from "@/lib/logger";
import type { RepositoryContext } from "../datastore/repository";
import { BaseRepository } from "../datastore/repository";

export type ConsoleActivityActorType = "console_user" | "member";

// `console_activity_logs` is Core's audit trail and lives in the member
// database, alongside the members it references. Not in db.d.ts, hence the
// `as any` table refs.
export const ConsoleActivityLogsRepository = (ctx: RepositoryContext) => {
  const { memberDatastore: datastore } = new BaseRepository(ctx);

  const FindEntriesByEntityIDs = async (params: {
    entityIDs: string[];
    featureSlugs?: string[];
    actorType?: ConsoleActivityActorType;
    limit?: number;
  }) => {
    const { entityIDs, featureSlugs, actorType, limit = 100 } = params;
    if (entityIDs.length === 0) {
      return [];
    }
    try {
      let featureIDs: string[] | undefined;
      if (featureSlugs?.length) {
        const features = await datastore
          .selectFrom("console_features")
          .select(["id"])
          .where("slug", "in", featureSlugs)
          .execute();
        featureIDs = features.map((feature) => feature.id);
        if (featureIDs.length === 0) {
          return [];
        }
      }

      let query = datastore
        .selectFrom("console_activity_logs" as any)
        .select([
          "id",
          "action",
          "entity_type",
          "entity_id",
          "before_state",
          "after_state",
          "delta",
          "status",
          "error_message",
          "ip_address",
          "user_agent",
          "metadata",
          "created_at",
          "actor_id",
          "actor_type",
        ] as any)
        .where("entity_id" as any, "in", entityIDs)
        .orderBy("created_at" as any, "desc")
        .limit(limit);

      if (featureIDs) {
        query = query.where("console_feature_id" as any, "in", featureIDs);
      }
      if (actorType) {
        query = query.where("actor_type" as any, "=", actorType);
      }

      const records = (await query.execute()) as any[];

      // Resolve the actor's name: console users and members live in separate
      // tables, same as Core does it.
      const result = [];
      for (const record of records) {
        if (record.actor_type === "console_user") {
          const userRecord = await datastore
            .selectFrom("console_users")
            .select(["first_name", "last_name"])
            .where("id", "=", record.actor_id)
            .executeTakeFirst();
          result.push({
            ...record,
            userDetails: {
              name: `${userRecord?.first_name ?? ""} ${userRecord?.last_name ?? ""}`.trim(),
              first_name: userRecord?.first_name ?? null,
              last_name: userRecord?.last_name ?? null,
            },
          });
          continue;
        }

        const memberRecord = await datastore
          .selectFrom("members")
          .innerJoin(
            "member_profiles",
            "member_profiles.member_id",
            "members.id",
          )
          .select(["member_profiles.first_name", "member_profiles.last_name"])
          .where("members.user_id", "=", record.actor_id)
          .executeTakeFirst();
        result.push({
          ...record,
          userDetails: {
            name: `${memberRecord?.first_name ?? ""} ${memberRecord?.last_name ?? ""}`.trim(),
            first_name: memberRecord?.first_name ?? null,
            last_name: memberRecord?.last_name ?? null,
          },
        });
      }
      return result;
    } catch (err) {
      logError(err);
      throw new DatabaseError({
        error: err,
        message: "Failed fetching console activity logs",
      });
    }
  };

  return { FindEntriesByEntityIDs };
};

export type TConsoleActivityLogsRepository = ReturnType<
  typeof ConsoleActivityLogsRepository
>;
