import type { CachePort } from "../cache/cache-port.js";
import { config } from "../../config.js";
import type {
  CreateFormInput,
  FormRecord,
  FormRepository,
  FormVersionRecord,
  FormWithVersions,
  PublishVersionInput,
  SubmissionFilter,
  SubmissionInput,
  SubmissionPage,
  SubmissionRecord,
  UpdateFormInput,
} from "./domain.js";

export class FormService {
  constructor(
    private readonly repo: FormRepository,
    private readonly cache: CachePort
  ) {}

  async listForms(): Promise<FormRecord[]> {
    const key = "forms:list";
    const cached = await this.cache.get<FormRecord[]>(key);
    if (cached) return cached;

    const forms = await this.repo.listForms();
    await this.cache.set(key, forms, config.schemaTtlSeconds);
    return forms;
  }

  async getFormByCode(code: string): Promise<FormWithVersions | null> {
    const key = `forms:detail:${code}`;
    const cached = await this.cache.get<FormWithVersions>(key);
    if (cached) return cached;

    const form = await this.repo.getFormByCode(code);
    if (form) {
      await this.cache.set(key, form, config.schemaTtlSeconds);
    }
    return form;
  }

  /**
   * Legacy-compat: return the surveyJson of the requested version.
   * Kept for the GET /api/forms/schema endpoint.
   */
  async getSchema(
    formCode: string,
    version: string
  ): Promise<Record<string, unknown>> {
    const cacheKey = `forms:schema:${formCode}:${version}`;
    const cached = await this.cache.get<Record<string, unknown>>(cacheKey);
    if (cached) return cached;

    const form = await this.getFormByCode(formCode);
    if (!form) throw new Error(`Form "${formCode}" not found`);

    const ver = form.versions.find((v) => v.semver === version);
    if (!ver) throw new Error(`Version "${version}" not found for form "${formCode}"`);

    await this.cache.set(cacheKey, ver.surveyJson, config.schemaTtlSeconds);
    return ver.surveyJson;
  }

  async createForm(data: CreateFormInput): Promise<FormRecord> {
    const form = await this.repo.createForm(data);
    await this.cache.delByPrefix("forms:list");
    return form;
  }

  async updateForm(code: string, data: UpdateFormInput): Promise<FormWithVersions | null> {
    const form = await this.repo.updateForm(code, data);
    if (form) {
      await Promise.all([
        this.cache.delByPrefix(`forms:detail:${code}`),
        this.cache.delByPrefix(`forms:schema:${code}:`),
        this.cache.delByPrefix("forms:list"),
      ]);
    }
    return form;
  }

  async deleteForm(code: string): Promise<boolean> {
    const deleted = await this.repo.softDeleteForm(code);
    if (deleted) {
      await Promise.all([
        this.cache.delByPrefix(`forms:detail:${code}`),
        this.cache.delByPrefix(`forms:schema:${code}:`),
        this.cache.delByPrefix("forms:list"),
      ]);
    }
    return deleted;
  }

  async publishVersion(
    formCode: string,
    data: PublishVersionInput
  ): Promise<FormVersionRecord> {
    const form = await this.repo.getFormByCode(formCode);
    if (!form) throw new Error(`Form "${formCode}" not found`);

    const version = await this.repo.publishVersion(form.id, data);

    // Invalidate caches
    await Promise.all([
      this.cache.delByPrefix(`forms:detail:${formCode}`),
      this.cache.delByPrefix(`forms:schema:${formCode}:`),
    ]);

    return version;
  }

  async submit(
    formCode: string,
    version: string,
    moduleCode: string,
    answers: Record<string, unknown>,
    submittedBy?: string
  ): Promise<SubmissionRecord> {
    const form = await this.getFormByCode(formCode);
    if (!form) throw new Error(`Form "${formCode}" not found`);

    const ver = form.versions.find((v) => v.semver === version);
    if (!ver) throw new Error(`Version "${version}" not found for form "${formCode}"`);

    // Validate answer keys against questionIndex
    const knownKeys = Object.keys(ver.questionIndex);
    if (knownKeys.length > 0) {
      for (const key of Object.keys(answers)) {
        if (!ver.questionIndex[key]) {
          throw new Error(`Unknown question key: ${key}`);
        }
      }
    }

    const input: SubmissionInput = {
      formId:         form.id,
      formVersionId:  ver.id,
      schemaChecksum: ver.schemaChecksum,
      moduleCode,
      submittedBy,
      answers,
    };
    return this.repo.saveSubmission(input);
  }

  async listSubmissions(formId: string, filter: SubmissionFilter): Promise<SubmissionPage> {
    return this.repo.listSubmissions(formId, filter);
  }
}
