import { createDbService } from '@/server/services/db.js';
/**
 * Vendor recommendations AI pipeline.
 *
 * Builds a prompt from aggregated vendor stats → calls Gemini → parses
 * structured JSON → persists top 3 recs to vendor_recommendations table.
 *
 * Called by the nightly vendorRecommendationsCron job.
 * Uses the same admin-LLM infra (createGeminiClient + generateTextWithUsage)
 * and API-key resolution (DB system_config overrides env GOOGLE_API_KEY).
 */

import type { DrizzleClient } from '@/server/db/client.js';
import { systemConfig, vendors } from '@/server/db/schema.js';
import { eq, inArray } from 'drizzle-orm';
import { createGeminiClient, generateTextWithUsage } from './gemini.js';
import {
  insertRecommendations,
  type VendorRecommendationKind,
} from '@/server/db/queries/vendor-recommendations.js';
import { getVendorFunnel, getPeriodComparison } from '@/server/db/queries/vendor-analytics.js';
import { formatShekelFloat } from '@/lib/money.js';
import { captureCaught } from '@/server/observability/capture.server';
import { getSystemConfig } from '@/server/db/queries/system-config.js';
import { requireConfiguredLlmModel } from './model-config.js';

// ─── Types ────────────────────────────────────────────────────────────────────

export interface VendorRecommendationPayload {
  title: string;
  body: string;
  actionLabel?: string;
  actionHref?: string;
  metadata?: Record<string, unknown>;
  [key: string]: unknown;
}

export interface ParsedRecommendation {
  kind: VendorRecommendationKind;
  payload: VendorRecommendationPayload;
}

export interface VendorRecCronEnv {
  DATABASE_URL: string;
  GOOGLE_API_KEY?: string;
}

// ─── Prompt construction ──────────────────────────────────────────────────────

const SYSTEM_PROMPT = `You are an AI assistant for Multideal, an Israeli marketplace platform.
Generate exactly 3 actionable recommendations for a vendor based on their performance data.
Each recommendation must be one of these kinds: stock_nudge, pricing_suggestion, schedule_opportunity.
Use all 3 kinds — one per recommendation.

Respond ONLY with a JSON array (no markdown, no extra text):
[
  {
    "kind": "stock_nudge" | "pricing_suggestion" | "schedule_opportunity",
    "title": "<short Hebrew title, max 60 chars>",
    "body": "<actionable Hebrew description, max 200 chars>",
    "actionLabel": "<optional CTA label in Hebrew>",
    "actionHref": "<optional relative URL like /vendor/deals/new>"
  },
  ...
]`;

export function buildVendorRecommendationPrompt(stats: {
  businessName: string;
  activeDeals: number;
  totalDeals: number;
  avgConversionRate: number;
  revenueCurrentPeriod: number;
  revenuePreviousPeriod: number;
  ordersCurrentPeriod: number;
  ordersPreviousPeriod: number;
  avgRating: number | null;
}): string {
  const conversionPct = (stats.avgConversionRate * 100).toFixed(1);
  const revDelta =
    stats.revenuePreviousPeriod > 0
      ? (
          ((stats.revenueCurrentPeriod - stats.revenuePreviousPeriod) /
            stats.revenuePreviousPeriod) *
          100
        ).toFixed(1)
      : 'N/A';

  return `${SYSTEM_PROMPT}

Vendor: ${stats.businessName}
Active deals: ${stats.activeDeals} / ${stats.totalDeals} total
Average conversion rate: ${conversionPct}%
Revenue (last 30 days): ${formatShekelFloat(stats.revenueCurrentPeriod)}
Revenue (prior 30 days): ${formatShekelFloat(stats.revenuePreviousPeriod)}
Revenue delta: ${revDelta}%
Orders (last 30 days): ${stats.ordersCurrentPeriod}
Orders (prior 30 days): ${stats.ordersPreviousPeriod}
${stats.avgRating != null ? `Average rating: ${stats.avgRating.toFixed(1)}/5` : 'Average rating: no data'}

Generate 3 recommendations now.`;
}

// ─── Response parsing ─────────────────────────────────────────────────────────

export function parseRecommendations(raw: string): ParsedRecommendation[] {
  // Strip markdown fences if model wraps output
  const cleaned = raw
    .replace(/^```(?:json)?\s*/i, '')
    .replace(/\s*```\s*$/, '')
    .trim();

  let parsed: unknown;
  try {
    parsed = JSON.parse(cleaned);
  } catch (err) {
    captureCaught(err, { scope: 'server.ai.vendor-recommendations', severity: 'warning' });
    throw new Error(`vendor-recommendations: failed to parse LLM JSON: ${cleaned.slice(0, 200)}`, {
      cause: err,
    });
  }

  if (!Array.isArray(parsed)) {
    throw new Error('vendor-recommendations: LLM response is not an array');
  }

  const VALID_KINDS = new Set<string>([
    'stock_nudge',
    'pricing_suggestion',
    'schedule_opportunity',
  ]);

  return parsed
    .filter(
      (item): item is Record<string, unknown> =>
        typeof item === 'object' &&
        item !== null &&
        typeof (item as Record<string, unknown>).kind === 'string' &&
        VALID_KINDS.has((item as Record<string, unknown>).kind as string) &&
        typeof (item as Record<string, unknown>).title === 'string' &&
        typeof (item as Record<string, unknown>).body === 'string',
    )
    .slice(0, 3)
    .map((item) => ({
      kind: item.kind as VendorRecommendationKind,
      payload: {
        title: String(item.title),
        body: String(item.body),
        actionLabel: item.actionLabel != null ? String(item.actionLabel) : undefined,
        actionHref: item.actionHref != null ? String(item.actionHref) : undefined,
      },
    }));
}

// ─── Per-vendor runner ────────────────────────────────────────────────────────

/**
 * Generate and persist recommendations for a single vendor.
 *
 * Silently skips vendors with no active deals (nothing to recommend on).
 * Throws on LLM or DB error — cron handles retries/logging per vendor.
 */
export async function generateRecommendationsForVendor(
  db: DrizzleClient,
  apiKey: string,
  vendorId: string,
  businessName: string,
): Promise<void> {
  // Gather stats
  const [funnel, comparison] = await Promise.all([
    getVendorFunnel(db, vendorId),
    getPeriodComparison(db, vendorId, 30),
  ]);

  // Skip vendors with no activity
  if (funnel.activeDeals === 0 && funnel.totalPurchases === 0) {
    return;
  }

  const prompt = buildVendorRecommendationPrompt({
    businessName,
    activeDeals: funnel.activeDeals,
    totalDeals: funnel.totalDeals,
    avgConversionRate: funnel.avgConversionRate,
    revenueCurrentPeriod: comparison.current.revenue,
    revenuePreviousPeriod: comparison.previous.revenue,
    ordersCurrentPeriod: comparison.current.orders,
    ordersPreviousPeriod: comparison.previous.orders,
    avgRating: comparison.current.avgRating,
  });

  const client = createGeminiClient(apiKey);
  const model = requireConfiguredLlmModel(
    await getSystemConfig(db, 'llm_model'),
    'vendor recommendations',
  );
  const fallbackModel = requireConfiguredLlmModel(
    await getSystemConfig(db, 'llm_fallback_model'),
    'vendor recommendations fallback',
  );
  const { text } = await generateTextWithUsage(client, prompt, model, fallbackModel);
  const recs = parseRecommendations(text);

  if (recs.length === 0) return;

  await insertRecommendations(
    db,
    recs.map((r) => ({
      vendorId,
      kind: r.kind,
      payload: r.payload as Record<string, unknown>,
    })),
  );
}

// ─── Cron runner ──────────────────────────────────────────────────────────────

/**
 * Nightly cron: generate recommendations for all active/veteran vendors.
 *
 * Registered in the cron dispatcher on the `0 3 * * *` schedule.
 * Errors per vendor are caught and logged — one failure does not skip others.
 */
export async function runVendorRecommendationsCron(env: VendorRecCronEnv): Promise<void> {
  const db = createDbService({ DATABASE_URL: env.DATABASE_URL });

  // Resolve API key: DB-stored overrides env secret
  const rows = await db
    .select({ value: systemConfig.value })
    .from(systemConfig)
    .where(eq(systemConfig.key, 'google_api_key'));
  const apiKey = rows[0]?.value ?? env.GOOGLE_API_KEY ?? null;

  if (!apiKey) {
    console.warn('vendor-recommendations-cron: no GOOGLE_API_KEY — skipping');
    return;
  }

  // Fetch all active/veteran vendors
  const activeVendors = await db
    .select({ id: vendors.id, businessName: vendors.businessName })
    .from(vendors)
    .where(inArray(vendors.accountState, ['ACTIVE', 'VETERAN']));

  for (const vendor of activeVendors) {
    try {
      await generateRecommendationsForVendor(db, apiKey, vendor.id, vendor.businessName);
    } catch (err) {
      console.error(`vendor-recommendations-cron: failed for vendor ${vendor.id}:`, err);
      captureCaught(err, {
        scope: 'server.ai.vendor-recommendations.cron',
        severity: 'warning',
        extra: { vendorId: vendor.id },
      });
    }
  }
}
