import {
  createOracSubmission,
  fetchAssessmentListProjection,
  fetchDefaultOracFormId,
  fetchFormIdForLocation,
  submitSnapObservation
} from '../../api.js';
import { clearPendingSnap } from './executorSnapObservationCache.js';

function instanceAnswerKey(instanceId, questionId) {
  return `i:${instanceId}:q:${questionId}`;
}

function buildSubmissionAnswers(sectionInstances, answersMap) {
  const rows = [];
  for (const inst of sectionInstances) {
    for (const q of inst.questions || []) {
      const rawId = q.question_id;
      const qid =
        typeof rawId === 'number' && Number.isInteger(rawId)
          ? rawId
          : parseInt(String(rawId ?? ''), 10);
      if (!Number.isInteger(qid) || qid <= 0) continue;
      const raw = answersMap[instanceAnswerKey(inst.instanceId, q.question_id)];
      rows.push({
        question_id: qid,
        answer_value: raw == null ? '' : String(raw),
        is_media: 0
      });
    }
  }
  return rows;
}

/** Read the insertedId directly from the API response — no fallback fetch needed. */
function snapIdFromApiRes(apiRes) {
  const raw = apiRes?.insertedId ?? apiRes?.snap_id;
  const id = raw != null ? Number(raw) : null;
  return Number.isInteger(id) && id > 0 ? id : null;
}

async function resolveEffectiveFormId(cachedFormId, resolvedAssessment) {
  if (cachedFormId != null && cachedFormId !== '') {
    const n = Number(cachedFormId);
    if (Number.isInteger(n) && n > 0) return n;
  }
  if (resolvedAssessment?.orac_form_id != null && resolvedAssessment.orac_form_id !== '') {
    const n = Number(resolvedAssessment.orac_form_id);
    if (Number.isInteger(n) && n > 0) return n;
  }
  const locId = resolvedAssessment?.location_id;
  if (locId == null || locId === '') return null;
  try {
    const { form_id: resolved } = await fetchFormIdForLocation(locId);
    if (resolved == null) return null;
    const n = Number(resolved);
    return Number.isInteger(n) && n > 0 ? n : null;
  } catch {
    return null;
  }
}

/**
 * Persist cached snap + observation answer sets to the database.
 *
 * Each queued observation becomes its OWN `orac_snaps` row (same marked image,
 * individual answers) so that every observation appears as a separate row in the
 * Risk Assignment Document.
 *
 * @param {object} payload - from sessionStorage (setPendingSnap)
 */
export async function submitPendingSnapObservationsToDatabase(payload) {
  const assessmentId = Number(payload.assessment_id);
  if (!Number.isInteger(assessmentId) || assessmentId <= 0) {
    throw new Error('Invalid assessment in cached snap.');
  }
  const observations = Array.isArray(payload.observations) ? payload.observations : [];
  if (observations.length === 0) {
    throw new Error('No observations in cache.');
  }

  const formIdRaw = payload.form_id ?? payload.formSchema?.form_id;
  const formIdForTiming =
    formIdRaw != null && formIdRaw !== '' ? Number(formIdRaw) : null;
  const submittedAt = new Date().toISOString();

  // Resolve form once — shared by all observations in this snap session.
  let resolvedAssessment = payload.resolvedAssessmentSnapshot || {};
  try {
    const projected = await fetchAssessmentListProjection(assessmentId);
    if (projected && typeof projected === 'object') {
      resolvedAssessment = { ...resolvedAssessment, ...projected };
    }
  } catch {
    /* use snapshot only */
  }

  let effectiveFormId = await resolveEffectiveFormId(formIdRaw, resolvedAssessment);
  if (effectiveFormId == null) {
    try {
      const { form_id: fallback } = await fetchDefaultOracFormId();
      if (fallback != null) {
        const n = Number(fallback);
        if (Number.isInteger(n) && n > 0) effectiveFormId = n;
      }
    } catch {
      /* optional */
    }
  }

  // Create ONE snap row per observation so each appears as its own row in the
  // Risk Assignment Document. All observations share the same marked image.
  let savedCount = 0;
  for (const obs of observations) {
    const obsInstances = (obs.sectionInstances || []).map((inst) => ({
      instance_id: inst.instanceId,
      section_id: inst.section_id,
      title: (inst.title || '').trim() || `Section ${inst.section_id}`,
      answers: Object.fromEntries(
        (inst.questions || []).map((q) => [
          String(q.question_id),
          (obs.answers || {})[instanceAnswerKey(inst.instanceId, q.question_id)] ?? ''
        ])
      )
    }));

    const res = await submitSnapObservation({
      assessment_id: assessmentId,
      snap_image: payload.markedImageDataUrl,
      risk_violation_time: payload.violationTime,
      marks: payload.marksJson,
      ...(Number.isInteger(formIdForTiming) && formIdForTiming > 0
        ? { form_id: formIdForTiming }
        : {}),
      section_instances: obsInstances,
      submitted_at: submittedAt
    });

    const snapId = snapIdFromApiRes(res);
    if (snapId == null) {
      throw new Error(
        `Observation ${savedCount + 1} snap was not saved (no insertedId returned).`
      );
    }

    const submissionAnswers = buildSubmissionAnswers(
      obs.sectionInstances || [],
      obs.answers || {}
    );
    if (submissionAnswers.length > 0) {
      await createOracSubmission({
        assessment_id: assessmentId,
        snap_id: snapId,
        ...(effectiveFormId != null ? { form_id: effectiveFormId } : {}),
        answers: submissionAnswers
      });
    }

    savedCount += 1;
  }

  clearPendingSnap();
  return { observationCount: savedCount };
}
