// Copyright 2026 CRMy Contributors
// SPDX-License-Identifier: Apache-2.0

import type { DbPool } from '../pool.js';
import type { ContextTypeRegistryEntry, UUID } from '@crmy/shared';

// ── Template definitions ────────────────────────────────────────────────────
// Each extractable type has:
//   json_schema       — JSON Schema for structured_data (shown to agents + used for validation)
//   extraction_prompt — Short instruction added to the extraction prompt for this type
//   is_extractable    — Whether the extraction pipeline should produce this type

interface ContextTypeTemplate extends Omit<ContextTypeRegistryEntry, 'tenant_id' | 'created_at'> {
  json_schema?: Record<string, unknown>;
  extraction_prompt?: string;
  is_extractable: boolean;
}

export const DEFAULT_CONTEXT_TYPES: ContextTypeTemplate[] = [
  // ── Unstructured (original types, no extraction schema) ─────────────────
  {
    type_name: 'note',
    label: 'Note',
    description: 'General-purpose note or observation',
    is_default: true,
    is_extractable: false,
    priority_weight: 0.7,
    confidence_half_life_days: null,
  },
  {
    type_name: 'transcript',
    label: 'Transcript',
    description: 'Verbatim or near-verbatim record of a conversation',
    is_default: true,
    is_extractable: false,
    priority_weight: 0.5,
    confidence_half_life_days: null,
  },
  {
    type_name: 'summary',
    label: 'Summary',
    description: 'Condensed version of a longer interaction or document',
    is_default: true,
    is_extractable: false,
    priority_weight: 1.2,
    confidence_half_life_days: null,
  },
  {
    type_name: 'research',
    label: 'Research',
    description: 'Background research on a person, company, or market',
    is_default: true,
    is_extractable: false,
    priority_weight: 0.8,
    confidence_half_life_days: null,
  },
  {
    type_name: 'preference',
    label: 'Preference',
    description: 'Known preference of a contact or account (communication style, timing, etc.)',
    is_default: true,
    is_extractable: false,
    priority_weight: 1.0,
    confidence_half_life_days: 180,
  },
  {
    type_name: 'decision',
    label: 'Decision',
    description: 'A decision that was made and the reasoning behind it',
    is_default: true,
    is_extractable: false,
    priority_weight: 1.0,
    confidence_half_life_days: null,
  },
  {
    type_name: 'relationship_map',
    label: 'Relationship Map',
    description: 'Key people, their roles, influence, and relationships to each other',
    is_default: true,
    is_extractable: false,
    priority_weight: 1.3,
    confidence_half_life_days: 365,
  },
  {
    type_name: 'agent_reasoning',
    label: 'Agent Reasoning',
    description: "An AI agent's internal reasoning or analysis about next steps",
    is_default: true,
    is_extractable: false,
    priority_weight: 0.6,
    confidence_half_life_days: null,
  },
  {
    type_name: 'sentiment_analysis',
    label: 'Sentiment Analysis',
    description: 'Assessment of prospect sentiment or engagement level',
    is_default: true,
    is_extractable: false,
    priority_weight: 1.0,
    confidence_half_life_days: 30,
  },

  // ── Extractable structured types ─────────────────────────────────────────

  {
    type_name: 'commitment',
    label: 'Commitment',
    description: 'A specific commitment made by the prospect or customer (budget, timeline, resource, decision)',
    is_default: true,
    is_extractable: true,
    priority_weight: 2.0,
    confidence_half_life_days: 90,
    extraction_prompt: 'Extract explicit commitments made by the prospect/customer — budget approvals, timeline confirmations, resource allocations, or specific decisions. Only extract clearly stated commitments, not wishes or possibilities.',
    json_schema: {
      type: 'object',
      properties: {
        commitment_type: {
          type: 'string',
          enum: ['budget_approved', 'timeline_confirmed', 'resource_allocated', 'decision_made', 'other'],
          description: 'Category of commitment',
        },
        committed_by: {
          type: 'string',
          description: 'Name and/or role of the person who made the commitment',
        },
        value: {
          type: 'string',
          description: 'Specific value, amount, or detail of the commitment',
        },
        due_date: {
          type: 'string',
          description: 'When this commitment is expected to be fulfilled (ISO date or relative expression)',
        },
      },
      required: ['commitment_type', 'committed_by', 'value'],
    },
  },

  {
    type_name: 'next_step',
    label: 'Next Step',
    description: 'A concrete action item agreed upon during an interaction',
    is_default: true,
    is_extractable: true,
    priority_weight: 1.8,
    confidence_half_life_days: 30,
    extraction_prompt: 'Extract agreed-upon next steps and action items — who will do what by when. Only include explicitly agreed actions, not suggestions.',
    json_schema: {
      type: 'object',
      properties: {
        action: {
          type: 'string',
          description: 'What needs to happen',
        },
        owner: {
          type: 'string',
          description: 'Who is responsible (name or role)',
        },
        due_date: {
          type: 'string',
          description: 'Target completion date (ISO date or relative)',
        },
        depends_on: {
          type: 'string',
          description: 'Any blocker or prerequisite',
        },
      },
      required: ['action', 'owner'],
    },
  },

  {
    type_name: 'stakeholder',
    label: 'Stakeholder',
    description: 'Information about a person involved in the deal — their role, influence, and sentiment',
    is_default: true,
    is_extractable: true,
    priority_weight: 1.5,
    confidence_half_life_days: 180,
    extraction_prompt: "Extract information about individuals mentioned in the activity — their role in the decision, attitude toward the deal, and level of influence. Create one entry per person if multiple people are discussed.",
    json_schema: {
      type: 'object',
      properties: {
        person_name: {
          type: 'string',
          description: 'Name of the individual',
        },
        role: {
          type: 'string',
          description: 'Job title or role in the organization',
        },
        influence: {
          type: 'string',
          enum: ['decision_maker', 'influencer', 'champion', 'evaluator', 'gatekeeper', 'end_user'],
          description: 'Their role in the buying/decision process',
        },
        sentiment: {
          type: 'string',
          enum: ['strong_advocate', 'supportive', 'neutral', 'skeptical', 'blocker'],
          description: 'Their attitude toward moving forward',
        },
        key_concern: {
          type: 'string',
          description: 'Their primary concern, question, or interest',
        },
      },
      required: ['person_name', 'role', 'influence', 'sentiment'],
    },
  },

  {
    type_name: 'deal_risk',
    label: 'Deal Risk',
    description: 'A risk or blocker that could jeopardize the deal or relationship',
    is_default: true,
    is_extractable: true,
    priority_weight: 2.0,
    confidence_half_life_days: 60,
    extraction_prompt: 'Extract risks, blockers, or red flags that could slow or kill the deal — budget cuts, internal politics, competitor threats, technical blockers, champion changes, or legal/compliance issues.',
    json_schema: {
      type: 'object',
      properties: {
        risk_type: {
          type: 'string',
          enum: ['budget', 'timeline', 'technical', 'champion_loss', 'competitive', 'legal_compliance', 'organizational', 'other'],
          description: 'Category of risk',
        },
        severity: {
          type: 'string',
          enum: ['critical', 'high', 'medium', 'low'],
          description: 'How likely this risk is to derail the deal',
        },
        description: {
          type: 'string',
          description: 'Specific details about the risk',
        },
        mitigation: {
          type: 'string',
          description: 'Agreed or suggested mitigation steps',
        },
      },
      required: ['risk_type', 'severity', 'description'],
    },
  },

  {
    type_name: 'competitive_intel',
    label: 'Competitive Intel',
    description: 'Information about competitors relevant to this deal or account',
    is_default: true,
    is_extractable: true,
    priority_weight: 1.5,
    confidence_half_life_days: 60,
    extraction_prompt: 'Extract information about competing vendors — who they are, whether they are actively competing, what the customer said about them, and any strengths/weaknesses mentioned.',
    json_schema: {
      type: 'object',
      properties: {
        competitor: {
          type: 'string',
          description: 'Competitor name',
        },
        status: {
          type: 'string',
          enum: ['actively_competing', 'shortlisted', 'eliminated', 'incumbent', 'mentioned'],
          description: 'Competitor status in this deal',
        },
        customer_concern: {
          type: 'string',
          description: 'Why the customer is considering this competitor or what they like about them',
        },
        our_differentiator: {
          type: 'string',
          description: 'Our key advantage vs. this competitor (if mentioned)',
        },
      },
      required: ['competitor', 'status'],
    },
  },

  {
    type_name: 'objection',
    label: 'Objection',
    description: 'A concern, pushback, or blocker raised by the prospect',
    is_default: true,
    is_extractable: true,
    priority_weight: 1.8,
    confidence_half_life_days: 45,
    extraction_prompt: 'Extract objections, concerns, or pushback raised by the prospect — price concerns, feature gaps, timing issues, internal resistance, etc. Note whether the objection was addressed.',
    json_schema: {
      type: 'object',
      properties: {
        objection_category: {
          type: 'string',
          enum: ['price', 'timing', 'features', 'trust', 'competition', 'internal_politics', 'technical', 'other'],
          description: 'Type of objection',
        },
        raised_by: {
          type: 'string',
          description: 'Who raised the objection (name or role)',
        },
        status: {
          type: 'string',
          enum: ['open', 'addressed', 'resolved', 'recurring'],
          description: 'Current state of the objection',
        },
        response: {
          type: 'string',
          description: 'How the objection was or could be addressed',
        },
      },
      required: ['objection_category', 'status'],
    },
  },

  {
    type_name: 'methodology_gap',
    label: 'Methodology Gap',
    description: 'Missing or unclear GTM qualification detail that should be resolved before agents rely on the deal context',
    is_default: true,
    is_extractable: true,
    priority_weight: 1.7,
    confidence_half_life_days: 45,
    extraction_prompt: 'Extract missing or unclear qualification details that matter to the revenue process — unknown buyer, unclear process, missing decision criteria, unresolved budget, security, legal, procurement, timeline, or success criteria. Use this when the source reveals a gap rather than a confirmed fact.',
    json_schema: {
      type: 'object',
      properties: {
        gap_area: {
          type: 'string',
          enum: ['buyer', 'process', 'criteria', 'budget', 'timeline', 'security', 'legal', 'procurement', 'success_criteria', 'other'],
          description: 'The area where GTM context is missing or unclear',
        },
        description: {
          type: 'string',
          description: 'What is missing, unclear, or needs verification',
        },
        needed_from: {
          type: 'string',
          description: 'Who or what team can clarify the gap, if known',
        },
        impact: {
          type: 'string',
          description: 'Why this gap matters for the account, opportunity, or customer workflow',
        },
      },
      required: ['gap_area', 'description'],
    },
  },

  {
    type_name: 'success_criteria',
    label: 'Success Criteria',
    description: 'Customer-defined outcomes, evaluation criteria, or value measures',
    is_default: true,
    is_extractable: true,
    priority_weight: 1.8,
    confidence_half_life_days: 120,
    extraction_prompt: 'Extract explicit customer success criteria, desired outcomes, evaluation requirements, measurable value targets, or acceptance criteria. Only include criteria stated by or attributed to the customer.',
    json_schema: {
      type: 'object',
      properties: {
        criterion: {
          type: 'string',
          description: 'The outcome, requirement, or value measure the customer cares about',
        },
        owner: {
          type: 'string',
          description: 'Person or team that owns or cares about this criterion',
        },
        measurement: {
          type: 'string',
          description: 'How success will be measured, if stated',
        },
        priority: {
          type: 'string',
          enum: ['critical', 'high', 'medium', 'low'],
          description: 'How important this criterion appears to be',
        },
      },
      required: ['criterion'],
    },
  },

  {
    type_name: 'buying_process',
    label: 'Buying Process',
    description: 'Decision path, approval process, procurement, legal, security, and committee dynamics',
    is_default: true,
    is_extractable: true,
    priority_weight: 1.9,
    confidence_half_life_days: 90,
    extraction_prompt: 'Extract information about how the customer will buy, approve, or evaluate the solution — decision process, procurement, legal, security, approval steps, buying committee, timeline gates, or required reviews.',
    json_schema: {
      type: 'object',
      properties: {
        process_step: {
          type: 'string',
          description: 'The buying, approval, review, or evaluation step',
        },
        owner: {
          type: 'string',
          description: 'Person, role, or team responsible for this step',
        },
        status: {
          type: 'string',
          enum: ['not_started', 'in_progress', 'blocked', 'completed', 'unknown'],
          description: 'Current status of the process step',
        },
        due_date: {
          type: 'string',
          description: 'When this step should happen or complete',
        },
      },
      required: ['process_step', 'status'],
    },
  },

  {
    type_name: 'forecast_signal',
    label: 'Forecast Signal',
    description: 'Timing, confidence, push/pull, close risk, or forecast-relevant change',
    is_default: true,
    is_extractable: true,
    priority_weight: 2.0,
    confidence_half_life_days: 30,
    extraction_prompt: 'Extract forecast-relevant changes such as a deal pulling forward, slipping, close confidence increasing/decreasing, buyer urgency, timing risk, or changes that could affect forecast category. Treat these as Signals that usually need review before affecting forecast.',
    json_schema: {
      type: 'object',
      properties: {
        signal_type: {
          type: 'string',
          enum: ['pull_forward', 'push_out', 'confidence_up', 'confidence_down', 'timing_risk', 'scope_change', 'other'],
          description: 'Type of forecast-relevant signal',
        },
        direction: {
          type: 'string',
          enum: ['positive', 'negative', 'neutral', 'unknown'],
          description: 'Whether the signal improves, hurts, or does not clearly change forecast confidence',
        },
        reason: {
          type: 'string',
          description: 'Why this affects forecast or timing',
        },
        effective_date: {
          type: 'string',
          description: 'Relevant date or period, if stated',
        },
      },
      required: ['signal_type', 'direction', 'reason'],
    },
  },

  {
    type_name: 'meeting_notes',
    label: 'Meeting Notes',
    description: 'Structured takeaways from a meeting (distinct from raw transcript)',
    is_default: true,
    is_extractable: false, // written explicitly, not extracted
    priority_weight: 1.2,
    confidence_half_life_days: null,
  },

  {
    type_name: 'key_fact',
    label: 'Key Fact',
    description: 'An important fact about the contact, account, or deal that does not fit another category',
    is_default: true,
    is_extractable: true,
    priority_weight: 1.3,
    confidence_half_life_days: null,
    extraction_prompt: 'Extract important standalone facts that do not fit the other categories — company news, org changes, technology stack details, budget cycles, contract end dates, etc.',
  },
];

// ── DB functions ─────────────────────────────────────────────────────────────

export async function seedDefaults(db: DbPool, tenantId: UUID): Promise<void> {
  for (const entry of DEFAULT_CONTEXT_TYPES) {
    await db.query(
      `INSERT INTO context_type_registry
       (type_name, tenant_id, label, description, is_default, json_schema,
        extraction_prompt, is_extractable, priority_weight, confidence_half_life_days)
     VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
       ON CONFLICT (tenant_id, type_name) DO UPDATE SET
         label                     = EXCLUDED.label,
         description               = EXCLUDED.description,
         json_schema               = EXCLUDED.json_schema,
         extraction_prompt         = EXCLUDED.extraction_prompt,
         is_extractable            = EXCLUDED.is_extractable,
         priority_weight           = EXCLUDED.priority_weight,
         confidence_half_life_days = EXCLUDED.confidence_half_life_days`,
      [
        entry.type_name,
        tenantId,
        entry.label,
        entry.description ?? null,
        true,
        entry.json_schema ? JSON.stringify(entry.json_schema) : null,
        entry.extraction_prompt ?? null,
        entry.is_extractable,
        entry.priority_weight,
        entry.confidence_half_life_days ?? null,
      ],
    );
  }
}

export async function ensureDefaultContextTypes(db: DbPool, tenantId: UUID): Promise<void> {
  for (const entry of DEFAULT_CONTEXT_TYPES) {
    await db.query(
      `INSERT INTO context_type_registry
       (type_name, tenant_id, label, description, is_default, json_schema,
        extraction_prompt, is_extractable, priority_weight, confidence_half_life_days)
     VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
       ON CONFLICT (tenant_id, type_name) DO NOTHING`,
      [
        entry.type_name,
        tenantId,
        entry.label,
        entry.description ?? null,
        true,
        entry.json_schema ? JSON.stringify(entry.json_schema) : null,
        entry.extraction_prompt ?? null,
        entry.is_extractable,
        entry.priority_weight,
        entry.confidence_half_life_days ?? null,
      ],
    );
  }
}

export async function listContextTypes(
  db: DbPool,
  tenantId: UUID,
): Promise<ContextTypeRegistryEntry[]> {
  await ensureDefaultContextTypes(db, tenantId);
  let result = await db.query(
    'SELECT * FROM context_type_registry WHERE tenant_id = $1 ORDER BY type_name',
    [tenantId],
  );
  if (result.rows.length === 0) {
    await seedDefaults(db, tenantId);
    result = await db.query(
      'SELECT * FROM context_type_registry WHERE tenant_id = $1 ORDER BY type_name',
      [tenantId],
    );
  }
  return result.rows as ContextTypeRegistryEntry[];
}

/** Get only extractable types with their schemas and prompts. */
export async function getExtractableTypes(
  db: DbPool,
  tenantId: UUID,
): Promise<(ContextTypeRegistryEntry & { json_schema: Record<string, unknown> | null; extraction_prompt: string | null })[]> {
  await ensureDefaultContextTypes(db, tenantId);
  let result = await db.query(
    `SELECT * FROM context_type_registry
     WHERE tenant_id = $1 AND is_extractable = true
     ORDER BY type_name`,
    [tenantId],
  );
  if (result.rows.length === 0) {
    await seedDefaults(db, tenantId);
    result = await db.query(
      `SELECT * FROM context_type_registry
       WHERE tenant_id = $1 AND is_extractable = true
       ORDER BY type_name`,
      [tenantId],
    );
  }
  return result.rows as (ContextTypeRegistryEntry & { json_schema: Record<string, unknown> | null; extraction_prompt: string | null })[];
}

/** Get schema for a single context type (used for validation). */
export async function getContextTypeSchema(
  db: DbPool,
  tenantId: UUID,
  typeName: string,
): Promise<Record<string, unknown> | null> {
  const result = await db.query(
    'SELECT json_schema FROM context_type_registry WHERE tenant_id = $1 AND type_name = $2',
    [tenantId, typeName],
  );
  return (result.rows[0]?.json_schema as Record<string, unknown>) ?? null;
}

export async function addContextType(
  db: DbPool,
  tenantId: UUID,
  data: {
    type_name: string;
    label: string;
    description?: string;
    json_schema?: Record<string, unknown>;
    extraction_prompt?: string;
    is_extractable?: boolean;
    priority_weight?: number;
    confidence_half_life_days?: number | null;
  },
): Promise<ContextTypeRegistryEntry> {
  const result = await db.query(
    `INSERT INTO context_type_registry
       (type_name, tenant_id, label, description, is_default, json_schema,
        extraction_prompt, is_extractable, priority_weight, confidence_half_life_days)
     VALUES ($1, $2, $3, $4, FALSE, $5, $6, $7, $8, $9)
     RETURNING *`,
    [
      data.type_name,
      tenantId,
      data.label,
      data.description ?? null,
      data.json_schema ? JSON.stringify(data.json_schema) : null,
      data.extraction_prompt ?? null,
      data.is_extractable ?? false,
      data.priority_weight ?? 1.0,
      data.confidence_half_life_days ?? null,
    ],
  );
  return result.rows[0] as ContextTypeRegistryEntry;
}

/**
 * Return a map of context_type → { priority_weight, confidence_half_life_days }
 * used by the briefing service for priority scoring.
 */
export async function getTypeWeightsMap(
  db: DbPool,
  tenantId: UUID,
): Promise<Map<string, { priority_weight: number; confidence_half_life_days: number | null }>> {
  const result = await db.query(
    `SELECT type_name, priority_weight, confidence_half_life_days
     FROM context_type_registry WHERE tenant_id = $1`,
    [tenantId],
  );
  const map = new Map<string, { priority_weight: number; confidence_half_life_days: number | null }>();
  for (const row of result.rows as Array<{ type_name: string; priority_weight: number; confidence_half_life_days: number | null }>) {
    map.set(row.type_name, {
      priority_weight: row.priority_weight ?? 1.0,
      confidence_half_life_days: row.confidence_half_life_days ?? null,
    });
  }
  return map;
}

export async function removeContextType(
  db: DbPool,
  tenantId: UUID,
  typeName: string,
): Promise<boolean> {
  const result = await db.query(
    `DELETE FROM context_type_registry
     WHERE type_name = $1 AND tenant_id = $2 AND is_default = FALSE
     RETURNING type_name`,
    [typeName, tenantId],
  );
  return result.rows.length > 0;
}
