/**
 * Careers Domain Adapter
 *
 * Routes job_listings (jobs), job_applications, interview_bookings,
 * and interview_booking_tokens CRUD to Prisma or Convex.
 *
 * Convex path does NOT resolve relation joins (job_applications, jobs on
 * interview_bookings) -- the service layer handles that separately.
 */

import prisma from "@/lib/db";
import { convexQuery, convexMutation } from "@/lib/convex-client";
import { getDataSource } from "@/lib/data-source";
import {
  convexToPrismaShape,
  convexToPrismaShapeMany,
  applyPagination,
  dateToConvexTimestamp,
  type ConvexDoc,
  type BatchResult,
} from "./base";

const DOMAIN = "careers" as const;
const BOOKING_TS_FIELDS = [
  "createdAt",
  "updatedAt",
  "preferredDate",
  "confirmedDate",
  "confirmedAt",
  "cancelledAt",
];
const APPLICATION_TS_FIELDS = [
  "createdAt",
  "submittedAt",
  "reviewedAt",
  "hiredAt",
  "statusUpdatedAt",
  "interviewScheduledAt",
];
const JOB_TS_FIELDS = ["createdAt", "updatedAt", "openingAt", "closingAt"];
const BOOKING_TOKEN_TS_FIELDS = [
  "createdAt",
  "updatedAt",
  "expiresAt",
  "usedAt",
];

function isConvex(): boolean {
  return getDataSource(DOMAIN) === "convex";
}

// =========================================================================
// Job Listings (jobs table)
// =========================================================================

const jobListingsAdapter = {
  async findMany(args: {
    where?: Record<string, unknown>;
    include?: Record<string, unknown>;
    orderBy?: Record<string, string> | Record<string, string>[];
    take?: number;
    skip?: number;
  }) {
    if (!isConvex()) {
      return prisma.jobs.findMany(args as any);
    }

    const where = (args.where || {}) as Record<string, unknown>;
    const status = (where.status as string) || undefined;
    const department = where.department as string | undefined;

    const docs =
      (await convexQuery<ConvexDoc[]>("careers/queries:getJobs", {
        ...(status ? { status } : {}),
        ...(department ? { department } : {}),
      })) || [];

    const paginated = applyPagination(docs, args.skip, args.take);
    return convexToPrismaShapeMany(paginated, JOB_TS_FIELDS);
  },

  async findUnique(args: {
    where: { id: string };
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.jobs.findUnique(args as any);
    }

    // No getJobById Convex query -- fetch all and filter
    const docs =
      (await convexQuery<ConvexDoc[]>("careers/queries:getJobs", {})) || [];

    const match = docs.find((d) => (d.prismaId || d._id) === args.where.id);
    return match ? convexToPrismaShape(match, JOB_TS_FIELDS) : null;
  },

  async findFirst(args: {
    where?: Record<string, unknown>;
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.jobs.findFirst(args as any);
    }

    const where = (args.where || {}) as Record<string, unknown>;
    const status = where.status as string | undefined;
    const department = where.department as string | undefined;

    const docs =
      (await convexQuery<ConvexDoc[]>("careers/queries:getJobs", {
        ...(status ? { status } : {}),
        ...(department ? { department } : {}),
      })) || [];

    // Apply remaining filters
    const match = docs.find((doc) => {
      for (const [key, value] of Object.entries(where)) {
        if (key === "id") {
          if ((doc.prismaId || doc._id) !== value) return false;
        } else if (key === "status" || key === "department") {
          continue; // Already filtered by Convex query
        } else if (doc[key] !== value) {
          return false;
        }
      }
      return true;
    });

    return match ? convexToPrismaShape(match, JOB_TS_FIELDS) : null;
  },

  async create(args: {
    data: Record<string, unknown>;
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.jobs.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "careers/mutations:createJob",
      {
        ...(d.careerId ? { careerId: d.careerId as string } : {}),
        slug: (d.slug as string) || "",
        titleTranslations: d.titleTranslations ?? {},
        department: (d.department as string) || "",
        departmentLabel: d.departmentLabel ?? {},
        locationTranslations: d.locationTranslations ?? {},
        ...(d.contractType ? { contractType: d.contractType as string } : {}),
        ...(d.level ? { level: d.level as string } : {}),
        ...(d.responsibilities ? { responsibilities: d.responsibilities } : {}),
      },
    );

    return {
      id: (d.id as string) || convexId || "",
      careerId: d.careerId ?? null,
      slug: d.slug ?? "",
      titleTranslations: d.titleTranslations ?? {},
      department: d.department ?? "",
      departmentLabel: d.departmentLabel ?? {},
      locationTranslations: d.locationTranslations ?? {},
      contractType: d.contractType ?? "full_time",
      level: d.level ?? "mid",
      status: d.status ?? "DRAFT",
      createdAt: new Date(),
      updatedAt: new Date(),
    };
  },

  async update(args: {
    where: { id: string };
    data: Record<string, unknown>;
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.jobs.update(args as any);
    }

    const d = args.data;
    // Only status updates are supported via Convex mutations currently
    if (d.status) {
      await convexMutation("careers/mutations:updateJobStatus", {
        id: args.where.id,
        status: d.status as string,
      });
    }

    const existing = await jobListingsAdapter.findUnique({
      where: { id: args.where.id },
    });
    return existing ? { ...existing, ...d } : null;
  },

  async count(args?: { where?: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.jobs.count(args as any);
    }

    const where = (args?.where || {}) as Record<string, unknown>;
    const status = where.status as string | undefined;
    const department = where.department as string | undefined;

    const docs =
      (await convexQuery<ConvexDoc[]>("careers/queries:getJobs", {
        ...(status ? { status } : {}),
        ...(department ? { department } : {}),
      })) || [];

    return docs.length;
  },
};

// =========================================================================
// Job Applications
// =========================================================================

const jobApplicationsAdapter = {
  async findMany(args: {
    where?: Record<string, unknown>;
    include?: Record<string, unknown>;
    orderBy?: Record<string, string> | Record<string, string>[];
    take?: number;
    skip?: number;
  }) {
    if (!isConvex()) {
      return prisma.job_applications.findMany(args as any);
    }

    const where = (args.where || {}) as Record<string, unknown>;
    const jobId = where.jobId as string | undefined;
    const status = where.status as string | undefined;
    const limit = (args.take || 50) + (args.skip || 0);

    const docs =
      (await convexQuery<ConvexDoc[]>("careers/queries:getApplications", {
        ...(jobId ? { jobId } : {}),
        ...(status ? { status } : {}),
        limit,
      })) || [];

    // Apply additional where filters not handled by the Convex query
    let filtered = docs;
    if (where.applicantEmail) {
      filtered = filtered.filter(
        (d) => d.applicantEmail === where.applicantEmail,
      );
    }

    const paginated = applyPagination(filtered, args.skip, args.take);
    return convexToPrismaShapeMany(paginated, APPLICATION_TS_FIELDS);
  },

  async findUnique(args: {
    where: { id: string };
    include?: Record<string, unknown>;
    select?: Record<string, boolean>;
  }) {
    if (!isConvex()) {
      return prisma.job_applications.findUnique(args as any);
    }

    // Fetch all and filter by id -- no getApplicationById query available
    const docs =
      (await convexQuery<ConvexDoc[]>("careers/queries:getApplications", {
        limit: 10000,
      })) || [];

    const match = docs.find((d) => (d.prismaId || d._id) === args.where.id);
    if (!match) return null;

    const shaped = convexToPrismaShape(match, APPLICATION_TS_FIELDS);

    // If select is provided, filter to requested fields
    if (args.select) {
      const selected: Record<string, unknown> = {};
      for (const [key, include] of Object.entries(args.select)) {
        if (include) {
          selected[key] = (shaped as Record<string, unknown>)[key];
        }
      }
      return selected;
    }

    return shaped;
  },

  async findFirst(args: {
    where?: Record<string, unknown>;
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.job_applications.findFirst(args as any);
    }

    const where = (args.where || {}) as Record<string, unknown>;
    const jobId = where.jobId as string | undefined;
    const status = where.status as string | undefined;

    const docs =
      (await convexQuery<ConvexDoc[]>("careers/queries:getApplications", {
        ...(jobId ? { jobId } : {}),
        ...(status ? { status } : {}),
        limit: 10000,
      })) || [];

    const match = docs.find((doc) => {
      for (const [key, value] of Object.entries(where)) {
        if (key === "id") {
          if ((doc.prismaId || doc._id) !== value) return false;
        } else if (key === "jobId" || key === "status") {
          continue; // Already filtered by Convex query
        } else if (doc[key] !== value) {
          return false;
        }
      }
      return true;
    });

    return match ? convexToPrismaShape(match, APPLICATION_TS_FIELDS) : null;
  },

  async create(args: {
    data: Record<string, unknown>;
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.job_applications.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "careers/mutations:createApplication",
      {
        ...(d.jobId ? { jobId: d.jobId as string } : {}),
        applicantName: (d.applicantName as string) || "",
        applicantEmail: (d.applicantEmail as string) || "",
        applicantPhone: (d.applicantPhone as string) || "",
        ...(d.coverNote ? { coverNote: d.coverNote as string } : {}),
        cvPath: (d.cvPath as string) || "",
        cvFilename: (d.cvFilename as string) || "",
        cvBytes: (d.cvBytes as number) || 0,
        ...(d.submissionLanguage
          ? { submissionLanguage: d.submissionLanguage as string }
          : {}),
      },
    );

    return {
      id: (d.id as string) || convexId || "",
      jobId: d.jobId ?? null,
      applicantName: d.applicantName,
      applicantEmail: d.applicantEmail,
      applicantPhone: d.applicantPhone,
      coverNote: d.coverNote ?? null,
      cvBucket: d.cvBucket ?? "applications",
      cvPath: d.cvPath,
      cvFilename: d.cvFilename,
      cvBytes: d.cvBytes ?? 0,
      cvMimeType: d.cvMimeType ?? "application/pdf",
      submissionLanguage: d.submissionLanguage ?? "en",
      submittedAt: new Date(),
      status: d.status ?? "NEW",
      createdAt: new Date(),
    };
  },

  async count(args?: { where?: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.job_applications.count(args as any);
    }

    const where = (args?.where || {}) as Record<string, unknown>;
    const jobId = where.jobId as string | undefined;
    const status = where.status as string | undefined;

    const docs =
      (await convexQuery<ConvexDoc[]>("careers/queries:getApplications", {
        ...(jobId ? { jobId } : {}),
        ...(status ? { status } : {}),
        limit: 10000,
      })) || [];

    return docs.length;
  },
};

// =========================================================================
// Interview Bookings
// =========================================================================

const interviewBookingsAdapter = {
  async findMany(args: {
    where?: Record<string, unknown>;
    include?: Record<string, unknown>;
    orderBy?: Record<string, string> | Record<string, string>[];
    take?: number;
    skip?: number;
  }) {
    if (!isConvex()) {
      return prisma.interview_bookings.findMany(args as any);
    }

    const where = (args.where || {}) as Record<string, unknown>;
    const applicationId = where.applicationId as string | undefined;
    const status = where.status as string | undefined;
    const limit = (args.take || 20) + (args.skip || 0);

    const docs =
      (await convexQuery<ConvexDoc[]>("careers/queries:getInterviewBookings", {
        ...(applicationId ? { applicationId } : {}),
        ...(status ? { status } : {}),
        limit,
      })) || [];

    const paginated = applyPagination(docs, args.skip, args.take);
    return convexToPrismaShapeMany(paginated, BOOKING_TS_FIELDS);
  },

  async findFirst(args: {
    where?: Record<string, unknown>;
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.interview_bookings.findFirst(args as any);
    }

    const where = (args.where || {}) as Record<string, unknown>;
    const applicationId = where.applicationId as string | undefined;

    const docs =
      (await convexQuery<ConvexDoc[]>("careers/queries:getInterviewBookings", {
        ...(applicationId ? { applicationId } : {}),
        limit: 10000,
      })) || [];

    // Handle complex where filters (e.g. status: { in: [...] })
    const match = docs.find((doc) => {
      for (const [key, value] of Object.entries(where)) {
        if (key === "applicationId") continue; // Already filtered by Convex
        if (key === "id") {
          if ((doc.prismaId || doc._id) !== value) return false;
        } else if (
          typeof value === "object" &&
          value !== null &&
          "in" in (value as Record<string, unknown>)
        ) {
          const inValues = (value as { in: string[] }).in;
          if (!inValues.includes(doc[key] as string)) return false;
        } else if (doc[key] !== value) {
          return false;
        }
      }
      return true;
    });

    return match ? convexToPrismaShape(match, BOOKING_TS_FIELDS) : null;
  },

  async findUnique(args: {
    where: { id: string };
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.interview_bookings.findUnique(args as any);
    }

    const docs =
      (await convexQuery<ConvexDoc[]>("careers/queries:getInterviewBookings", {
        limit: 10000,
      })) || [];

    const match = docs.find((d) => (d.prismaId || d._id) === args.where.id);
    return match ? convexToPrismaShape(match, BOOKING_TS_FIELDS) : null;
  },

  async create(args: {
    data: Record<string, unknown>;
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.interview_bookings.create(args as any);
    }

    // No createInterviewBooking Convex mutation yet -- fall back to prisma
    // TODO: Add createInterviewBooking mutation to Convex careers domain
    return prisma.interview_bookings.create(args as any);
  },

  async update(args: {
    where: { id: string };
    data: Record<string, unknown>;
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.interview_bookings.update(args as any);
    }

    // No updateInterviewBooking Convex mutation yet -- fall back to prisma
    // TODO: Add updateInterviewBooking mutation to Convex careers domain
    return prisma.interview_bookings.update(args as any);
  },

  async count(args?: { where?: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.interview_bookings.count(args as any);
    }

    const where = (args?.where || {}) as Record<string, unknown>;
    const applicationId = where.applicationId as string | undefined;
    const status = where.status as string | undefined;

    const docs =
      (await convexQuery<ConvexDoc[]>("careers/queries:getInterviewBookings", {
        ...(applicationId ? { applicationId } : {}),
        ...(status ? { status } : {}),
        limit: 10000,
      })) || [];

    return docs.length;
  },
};

// =========================================================================
// Interview Booking Tokens
// =========================================================================

const interviewBookingTokensAdapter = {
  async findUnique(args: {
    where: { id: string };
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.interview_booking_tokens.findUnique(args as any);
    }

    // No Convex query for tokens yet -- fall back to prisma
    // TODO: Add getTokenById query to Convex careers domain
    return prisma.interview_booking_tokens.findUnique(args as any);
  },

  async create(args: {
    data: Record<string, unknown>;
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.interview_booking_tokens.create(args as any);
    }

    // No Convex mutation for token creation yet -- fall back to prisma
    // TODO: Add createToken mutation to Convex careers domain
    return prisma.interview_booking_tokens.create(args as any);
  },

  async update(args: {
    where: { id: string };
    data: Record<string, unknown>;
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.interview_booking_tokens.update(args as any);
    }

    // No Convex mutation for token update yet -- fall back to prisma
    // TODO: Add updateToken mutation to Convex careers domain
    return prisma.interview_booking_tokens.update(args as any);
  },

  async count(args?: { where?: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.interview_booking_tokens.count(args as any);
    }

    // No Convex query for token count yet -- fall back to prisma
    // TODO: Add token count query to Convex careers domain
    return prisma.interview_booking_tokens.count(args as any);
  },
};

// --- Namespace exports ---------------------------------------------------

export {
  jobListingsAdapter,
  jobApplicationsAdapter,
  interviewBookingsAdapter,
  interviewBookingTokensAdapter,
};
