import {
  accounts,
  allocations,
  capacityCommitments,
  contacts,
  contractObligations,
  contracts,
  demandDeals,
  slaMetricTargets,
  slaTerms,
  supplyDeals,
  type Database,
} from '@pig/db';
import { isPageContext } from '@pig/core';
import { eq } from 'drizzle-orm';
import { z } from 'zod';
import type { PiggyChatContext } from './chat';
import { createPagePigTools } from './page-tools';
import { defineTool, type AgentTool } from './provider';
import { createAccountLifecycleTool } from './lifecycle-tools';

const noInput = z.object({}).strict();

/**
 * Interactive chat gets one scoped read tool and no ambient access.
 *
 * A record context gets `pig_get_record`, which takes no id and so cannot
 * pivot to another row. A page context gets the single tool that answers that
 * page — and never `pig_get_record`, because there is no record to read and a
 * tool that would throw is a wasted turn out of four.
 */
export function createInteractivePigTools(
  db: Database,
  context: PiggyChatContext | undefined,
): AgentTool[] {
  // No context is the dashboard case by another name: the same bounded
  // workspace overview, rather than a second definition that could drift.
  if (!context) return createPagePigTools(db, '/');
  if (isPageContext(context)) return createPagePigTools(db, context.route);
  if (context.type === 'account') {
    return [
      defineTool({
        name: 'pig_get_record',
        description:
          'Read the PIG record currently in focus and its directly related commercial data. ' +
          'This tool accepts no id and cannot inspect a different record.',
        inputSchema: noInput,
        execute: async () => readFocusedRecord(db, context),
      }),
      createAccountLifecycleTool(db, context.id),
    ];
  }
  return [
    defineTool({
      name: 'pig_get_record',
      description:
        'Read the PIG record currently in focus and its directly related commercial data. ' +
        'This tool accepts no id and cannot inspect a different record.',
      inputSchema: noInput,
      execute: async () => readFocusedRecord(db, context),
    }),
  ];
}

type PiggyRecordContext = Exclude<PiggyChatContext, { type: 'page' }>;

async function readFocusedRecord(db: Database, context: PiggyRecordContext): Promise<unknown> {
  if (context.type === 'account') {
    const [account] = await db.select().from(accounts).where(eq(accounts.id, context.id)).limit(1);
    if (!account) throw new Error('The account in focus no longer exists.');
    const [people, demand, supply, paperwork] = await Promise.all([
      db.select().from(contacts).where(eq(contacts.accountId, context.id)).limit(100),
      db.select().from(demandDeals).where(eq(demandDeals.accountId, context.id)).limit(100),
      db.select().from(supplyDeals).where(eq(supplyDeals.accountId, context.id)).limit(100),
      db.select().from(contracts).where(eq(contracts.accountId, context.id)).limit(100),
    ]);
    return { account, contacts: people, demandDeals: demand, supplyDeals: supply, contracts: paperwork };
  }

  if (context.type === 'contact') {
    const [contact] = await db.select().from(contacts).where(eq(contacts.id, context.id)).limit(1);
    if (!contact) throw new Error('The contact in focus no longer exists.');
    const [account] = contact.accountId
      ? await db.select().from(accounts).where(eq(accounts.id, contact.accountId)).limit(1)
      : [];
    return { contact, account: account ?? null };
  }

  if (context.type === 'demand_deal') {
    const [deal] = await db.select().from(demandDeals).where(eq(demandDeals.id, context.id)).limit(1);
    if (!deal) throw new Error('The demand deal in focus no longer exists.');
    const [account] = await db.select().from(accounts).where(eq(accounts.id, deal.accountId)).limit(1);
    const reservations = await db
      .select()
      .from(allocations)
      .where(eq(allocations.demandDealId, deal.id))
      .limit(100);
    return { deal, account: account ?? null, allocations: reservations };
  }

  if (context.type === 'supply_deal') {
    const [deal] = await db.select().from(supplyDeals).where(eq(supplyDeals.id, context.id)).limit(1);
    if (!deal) throw new Error('The supply deal in focus no longer exists.');
    const [account] = await db.select().from(accounts).where(eq(accounts.id, deal.accountId)).limit(1);
    const commitments = await db
      .select()
      .from(capacityCommitments)
      .where(eq(capacityCommitments.supplyDealId, deal.id))
      .limit(100);
    return { deal, account: account ?? null, commitments };
  }

  if (context.type === 'commitment') {
    const [commitment] = await db
      .select()
      .from(capacityCommitments)
      .where(eq(capacityCommitments.id, context.id))
      .limit(1);
    if (!commitment) throw new Error('The capacity commitment in focus no longer exists.');
    const reservations = await db
      .select()
      .from(allocations)
      .where(eq(allocations.capacityCommitmentId, commitment.id))
      .limit(100);
    return { commitment, allocations: reservations };
  }

  const [contract] = await db.select().from(contracts).where(eq(contracts.id, context.id)).limit(1);
  if (!contract) throw new Error('The contract in focus no longer exists.');
  const [serviceLevels, obligations] = await Promise.all([
    db.select().from(slaTerms).where(eq(slaTerms.contractId, contract.id)).limit(10),
    db
      .select()
      .from(contractObligations)
      .where(eq(contractObligations.contractId, contract.id))
      .limit(100),
  ]);
  const metrics = serviceLevels[0]
    ? await db
        .select()
        .from(slaMetricTargets)
        .where(eq(slaMetricTargets.slaTermId, serviceLevels[0].id))
        .limit(100)
    : [];
  return { contract, slaTerms: serviceLevels, slaMetricTargets: metrics, obligations };
}
