import { logger } from "@infra/logger";
import type { Job, JobSearchProfile } from "@shared/types";
import { LlmService } from "./llm/service";
import type { JsonSchemaDefinition } from "./llm/types";
import { resolveLlmModel } from "./modelSelection";
import { sanitizeProfileForPrompt } from "./scorer";

const COVER_LETTER_SCHEMA: JsonSchemaDefinition = {
  name: "cover_letter",
  schema: {
    type: "object",
    properties: {
      coverLetter: {
        type: "string",
        description:
          "Complete, ready-to-send cover letter (plain text, professional formatting with line breaks)",
      },
    },
    required: ["coverLetter"],
    additionalProperties: false,
  },
};

interface CoverLetterResponse {
  coverLetter: string;
}

function buildCoverLetterPrompt(
  job: Job,
  resumeProfile: Record<string, unknown>,
  searchProfile: JobSearchProfile | null,
): string {
  const parts: string[] = [];

  parts.push(
    "Write a polished, professional cover letter for this job application suitable for a senior candidate.",
  );
  parts.push("");
  parts.push("RULES:");
  parts.push(
    "- Length: 4-5 short paragraphs, maximum ~550 words; use clear paragraphs separated by line breaks",
  );
  parts.push(
    "- Open with the role title and company name, then state relevant years of experience and primary domain (from resume/preferences)",
  );
  parts.push(
    "- Tie 3-5 concrete points from the resume (employers, tools, scope, metrics if present) to requirements in the job description",
  );
  parts.push(
    "- If CANDIDATE PREFERENCES include aboutMe, weave one sentence that reinforces motivation or focus (without repeating the whole resume)",
  );
  parts.push(
    "- Lead with what makes the candidate a strong fit for THIS specific role",
  );
  parts.push(
    "- Reference specific requirements from the job description and match them to candidate experience",
  );
  parts.push(
    "- Be genuine and enthusiastic without being generic, overstated, or sycophantic",
  );
  parts.push(
    "- Use a professional tone appropriate for hiring managers and recruiters",
  );
  parts.push(
    "- Sign with the candidate's full name from resume basics.name when present; otherwise use a neutral closing without brackets",
  );
  parts.push(
    "- Do NOT include placeholder text like [Your Name], [Date], or similar",
  );
  parts.push(
    "- Do NOT make up experience, employers, metrics, or skills the candidate doesn't have",
  );
  parts.push(
    "- End with a concise closing thanking the reader and inviting next steps",
  );
  parts.push("");

  parts.push("=== JOB DETAILS ===");
  parts.push(`Title: ${job.title}`);
  parts.push(`Company: ${job.employer}`);
  if (job.location) parts.push(`Location: ${job.location}`);
  if (job.jobDescription) {
    const desc =
      job.jobDescription.length > 4000
        ? `${job.jobDescription.slice(0, 4000)}...`
        : job.jobDescription;
    parts.push(`Description:\n${desc}`);
  }

  parts.push("");
  parts.push("=== CANDIDATE RESUME (structured) ===");
  parts.push(JSON.stringify(resumeProfile, null, 2));

  if (searchProfile) {
    parts.push("");
    parts.push("=== CANDIDATE PREFERENCES (job search profile) ===");
    if (searchProfile.aboutMe.trim()) {
      parts.push(`About / narrative: ${searchProfile.aboutMe}`);
    }
    if (searchProfile.experienceLevel.trim()) {
      parts.push(`Experience level: ${searchProfile.experienceLevel}`);
    }
    if (searchProfile.targetRoles.length > 0) {
      parts.push(`Target roles: ${searchProfile.targetRoles.join(", ")}`);
    }
    if (searchProfile.mustHaveSkills.length > 0) {
      parts.push(
        `Must-have skills to emphasize: ${searchProfile.mustHaveSkills.join(", ")}`,
      );
    }
    if (searchProfile.niceToHaveSkills.length > 0) {
      parts.push(
        `Supporting skills (mention if they appear in resume): ${searchProfile.niceToHaveSkills.join(", ")}`,
      );
    }
    if (searchProfile.preferredWorkArrangement.length > 0) {
      parts.push(
        `Work arrangement preferences: ${searchProfile.preferredWorkArrangement.join(", ")}`,
      );
    }
    if (searchProfile.preferredLocations.length > 0) {
      parts.push(
        `Location preferences: ${searchProfile.preferredLocations.join(", ")}`,
      );
    }
    if (searchProfile.minimumSalary.trim()) {
      parts.push(`Salary expectation context: ${searchProfile.minimumSalary}`);
    }
    if (searchProfile.industriesToTarget.length > 0) {
      parts.push(
        `Industries of interest: ${searchProfile.industriesToTarget.join(", ")}`,
      );
    }
    if (searchProfile.industriesToAvoid.length > 0) {
      parts.push(
        `Do not position the candidate as seeking: ${searchProfile.industriesToAvoid.join(", ")}`,
      );
    }
    if (searchProfile.dealBreakers.length > 0) {
      parts.push(
        `Candidate deal-breakers (do NOT argue against these or claim fit where the job clearly violates them): ${searchProfile.dealBreakers.join(", ")}`,
      );
    }
  }

  return parts.join("\n");
}

export async function generateCoverLetter(
  job: Job,
  resumeProfile: Record<string, unknown>,
  searchProfile: JobSearchProfile | null,
): Promise<{ coverLetter: string }> {
  const model = await resolveLlmModel("scoring");

  const prompt = buildCoverLetterPrompt(
    job,
    sanitizeProfileForPrompt(resumeProfile, {
      maxExperienceItems: 8,
      maxProjectItems: 8,
    }),
    searchProfile,
  );

  const llm = new LlmService();
  const result = await llm.callJson<CoverLetterResponse>({
    model,
    messages: [{ role: "user", content: prompt }],
    jsonSchema: COVER_LETTER_SCHEMA,
    maxRetries: 1,
    jobId: job.id,
  });

  if (!result.success) {
    logger.error("Cover letter generation failed", {
      jobId: job.id,
      error: result.error,
    });
    throw new Error(`Cover letter generation failed: ${result.error}`);
  }

  const letter = result.data.coverLetter;
  if (!letter || typeof letter !== "string") {
    throw new Error("LLM returned empty cover letter");
  }

  return { coverLetter: letter };
}
