import pool from '../../db';
import { ContentPayload, PipelineResult, ScanFinding, RAINMAKER_PROPERTY_ID, COMPLIANCE_OFF_RESULT } from './types';
import { getActivePolicy } from './policy';
import { scanContent } from './scanner';
import { rewriteContent } from './rewriter';

export async function runCompliancePipeline(payload: ContentPayload): Promise<PipelineResult> {
  const propertyId = payload.propertyId || RAINMAKER_PROPERTY_ID;

  // ── Property isolation: only rainmaker_web is gated ──
  if (propertyId !== RAINMAKER_PROPERTY_ID) {
    return { ...COMPLIANCE_OFF_RESULT, propertyId };
  }

  const policy = await getActivePolicy(propertyId);
  if (!policy) {
    return { ...COMPLIANCE_OFF_RESULT, propertyId, note: 'no_active_policy' };
  }

  // ── Effective-date gating: only content created today+ is scanned ──
  if (policy.effectiveDate) {
    const effectiveDate = new Date(policy.effectiveDate);
    effectiveDate.setHours(0, 0, 0, 0);
    const now = new Date();
    now.setHours(0, 0, 0, 0);
    if (now < effectiveDate) {
      return { ...COMPLIANCE_OFF_RESULT, propertyId, note: 'before_effective_date' };
    }
  }

  // Step 1: Scan all fields
  const scanResult = await scanContent(payload.fields, policy);
  const originalText = JSON.stringify(payload.fields);

  // Step 2: If PASS, persist and return
  if (scanResult.status === 'PASS') {
    const scanId = await persistScan({
      propertyId,
      contentId: payload.contentId || null,
      contentType: payload.contentType,
      contentSource: payload.contentSource || null,
      policyVersion: policy.version,
      originalText,
      severityScore: scanResult.severityScore,
      riskScoreAfter: 0,
      findings: scanResult.findings,
      rewrittenText: null,
      rewriteAttempted: false,
      rewritePassed: null,
      finalStatus: 'PASS',
    });

    return {
      scanId,
      propertyId,
      originalStatus: 'PASS',
      finalStatus: 'PASS',
      severityScore: scanResult.severityScore,
      riskScoreAfter: 0,
      findings: scanResult.findings,
      rewrittenText: null,
      rewriteAttempted: false,
      rewritePassed: null,
    };
  }

  // Step 3: WARN or BLOCK — attempt rewrite per field
  const rewrittenFields: Record<string, string> = { ...payload.fields };
  let rewriteAttempted = false;
  let anyRewriteFailed = false;

  // Group findings by field
  const findingsByField = new Map<string, ScanFinding[]>();
  for (const finding of scanResult.findings) {
    const arr = findingsByField.get(finding.field) || [];
    arr.push(finding);
    findingsByField.set(finding.field, arr);
  }

  // Rewrite each flagged field
  for (const [fieldName, fieldFindings] of findingsByField) {
    rewriteAttempted = true;
    const original = payload.fields[fieldName];
    if (!original) continue;

    const rewritten = await rewriteContent(
      original,
      fieldFindings,
      fieldName,
      policy.rewriteVoicePrompt
    );

    if (rewritten) {
      rewrittenFields[fieldName] = rewritten;
    } else {
      anyRewriteFailed = true;
    }
  }

  // Step 4: Re-scan rewritten content
  let finalStatus: 'PASS' | 'WARN' | 'BLOCK' = scanResult.status;
  let rewritePassed: boolean | null = null;
  let rewrittenText: string | null = null;
  let riskScoreAfter: number | null = null;

  if (rewriteAttempted && !anyRewriteFailed) {
    const rescan = await scanContent(rewrittenFields, policy);
    riskScoreAfter = rescan.severityScore;

    // Check if all BLOCK-level hits are cleared (no individual BLOCK finding remaining)
    const blockHitsAfter = rescan.findings.filter(f => f.severity >= 10);
    rewritePassed = blockHitsAfter.length === 0;
    rewrittenText = JSON.stringify(rewrittenFields);

    if (rewritePassed) {
      finalStatus = rescan.status === 'BLOCK' ? 'WARN' : rescan.status;
      // If all findings cleared, it's a PASS
      if (rescan.severityScore === 0) finalStatus = 'PASS';
    }
  } else if (rewriteAttempted) {
    rewritePassed = false;
    rewrittenText = JSON.stringify(rewrittenFields);
  }

  // Step 5: Persist scan + findings
  const scanId = await persistScan({
    propertyId,
    contentId: payload.contentId || null,
    contentType: payload.contentType,
    contentSource: payload.contentSource || null,
    policyVersion: policy.version,
    originalText,
    severityScore: scanResult.severityScore,
    riskScoreAfter,
    findings: scanResult.findings,
    rewrittenText,
    rewriteAttempted,
    rewritePassed,
    finalStatus,
  });

  // Persist individual rule hits
  await persistRuleHits(scanId, scanResult.findings);

  return {
    scanId,
    propertyId,
    originalStatus: scanResult.status,
    finalStatus,
    severityScore: scanResult.severityScore,
    riskScoreAfter,
    findings: scanResult.findings,
    rewrittenText,
    rewriteAttempted,
    rewritePassed,
  };
}

async function persistScan(data: {
  propertyId: string;
  contentId: string | null;
  contentType: string;
  contentSource: string | null;
  policyVersion: number;
  originalText: string;
  severityScore: number;
  riskScoreAfter: number | null;
  findings: ScanFinding[];
  rewrittenText: string | null;
  rewriteAttempted: boolean;
  rewritePassed: boolean | null;
  finalStatus: string;
}): Promise<string> {
  const { rows } = await pool.query(
    `INSERT INTO rm_compliance_scans
       (property_id, content_id, content_type, content_source, policy_version, original_text,
        severity_score, risk_score_after, findings, rewritten_text, rewrite_attempted, rewrite_passed, final_status)
     VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
     RETURNING id`,
    [
      data.propertyId,
      data.contentId,
      data.contentType,
      data.contentSource,
      data.policyVersion,
      data.originalText,
      data.severityScore,
      data.riskScoreAfter,
      JSON.stringify(data.findings),
      data.rewrittenText,
      data.rewriteAttempted,
      data.rewritePassed,
      data.finalStatus,
    ]
  );
  return rows[0].id;
}

async function persistRuleHits(scanId: string, findings: ScanFinding[]): Promise<void> {
  if (findings.length === 0) return;

  const values: any[] = [];
  const placeholders: string[] = [];
  let idx = 1;

  for (const f of findings) {
    placeholders.push(`($${idx}, $${idx + 1}, $${idx + 2}, $${idx + 3}, $${idx + 4}, $${idx + 5}, $${idx + 6})`);
    values.push(scanId, f.ruleId, f.matchedText.substring(0, 500), f.severity, f.field, f.position.start, f.position.end);
    idx += 7;
  }

  await pool.query(
    `INSERT INTO rm_compliance_rule_hits
       (scan_id, rule_id, matched_text, severity, field, position_start, position_end)
     VALUES ${placeholders.join(', ')}`,
    values
  );
}

export async function applyRewrite(
  scanId: string,
  applyMode: 'REPLACE_CONTENT' | 'SAVE_DRAFT' | 'REPLACE_PUBLIC_COPY' | 'SAVE_BLOCKED_DRAFT'
): Promise<{ success: boolean; error?: string }> {
  // Get scan record
  const { rows: scans } = await pool.query(
    'SELECT * FROM rm_compliance_scans WHERE id = $1',
    [scanId]
  );

  if (scans.length === 0) {
    return { success: false, error: 'Scan not found' };
  }

  const scan = scans[0];

  // Property isolation: only apply rewrites for rainmaker_web
  if (scan.property_id !== RAINMAKER_PROPERTY_ID) {
    return { success: false, error: 'Compliance apply is only supported for rainmaker_web' };
  }

  if (!scan.rewritten_text) {
    return { success: false, error: 'No rewritten text available' };
  }

  let rewrittenFields: Record<string, string>;
  try {
    rewrittenFields = JSON.parse(scan.rewritten_text);
  } catch {
    return { success: false, error: 'Invalid rewritten text format' };
  }

  // Normalize apply mode
  const isPublish = applyMode === 'REPLACE_CONTENT' || applyMode === 'REPLACE_PUBLIC_COPY';

  if (scan.content_type === 'blog_post' && scan.content_id) {
    const status = isPublish ? 'published' : 'draft';
    const publishedAt = isPublish ? 'NOW()' : 'NULL';

    await pool.query(
      `UPDATE rm_blog_posts SET
        title = COALESCE($1, title),
        meta_description = COALESCE($2, meta_description),
        content = COALESCE($3, content),
        excerpt = COALESCE($4, excerpt),
        status = $5,
        published_at = ${publishedAt},
        updated_at = NOW()
      WHERE id = $6`,
      [
        rewrittenFields.title || null,
        rewrittenFields.metaDescription || rewrittenFields.meta_description || null,
        rewrittenFields.content || null,
        rewrittenFields.excerpt || null,
        status,
        scan.content_id,
      ]
    );
  }

  // Mark scan as applied
  await pool.query(
    'UPDATE rm_compliance_scans SET applied_at = NOW() WHERE id = $1',
    [scanId]
  );

  return { success: true };
}
