/**
 * PIG — the domain ontology for a two-sided AI-compute go-to-market team.
 *
 * This file is the single source of truth for the vocabulary of the product.
 * The database schema, the API contracts, the UI, and the MCP tool surface all
 * derive their enums from here, so that a stage or a tier means exactly one
 * thing everywhere.
 *
 * Where a value set was taken from how this market demonstrably operates rather
 * than invented, the source is cited in a comment. Fidelity to the real motion
 * matters more than tidiness: a CRM whose stages do not match how deals
 * actually move is abandoned within a quarter.
 */

// ---------------------------------------------------------------------------
// Teams
// ---------------------------------------------------------------------------

/**
 * A two-sided compute business has three constituencies competing for the same
 * scarce GPUs. Research is deliberately first-class: internal research burn is
 * real capacity consumption, and margin math that cannot see it is wrong.
 */
export const TEAMS = ['supply', 'demand', 'research'] as const;
export type Team = (typeof TEAMS)[number];

export const TEAM_LABELS: Record<Team, string> = {
  supply: 'Supply',
  demand: 'Demand',
  research: 'Research',
};

export const TEAM_DESCRIPTIONS: Record<Team, string> = {
  supply: 'Sources, qualifies, prices and contracts GPU capacity from providers.',
  demand: 'Sells compute and post-training; renews and expands accounts.',
  research: 'Consumes capacity internally. Real burn, no revenue.',
};

/**
 * Role within a team. Authorization is team-scoped, never global by default.
 *
 * Listed in ascending rank, matching the Postgres enum's sort order — see
 * `ROLE_RANK` in permissions.ts, which is the authority. `viewer` exists for
 * the analyst, the executive and the outside contractor: people who must read
 * the book and must never write to it. It sits below `member` precisely so
 * that introducing it grants nothing, every existing rule requiring `member`
 * or higher.
 */
export const TEAM_ROLES = ['viewer', 'member', 'lead', 'admin'] as const;
export type TeamRole = (typeof TEAM_ROLES)[number];

// ---------------------------------------------------------------------------
// Accounts
// ---------------------------------------------------------------------------

/**
 * An account may sit on either side of the market — or both. Neoclouds
 * routinely sell capacity to an aggregator while also buying managed training
 * from it, so `both` is a real state and not a data-entry error.
 */
export const ACCOUNT_SIDES = ['supply', 'demand', 'both'] as const;
export type AccountSide = (typeof ACCOUNT_SIDES)[number];

/**
 * Supply-side counterparty archetypes. These behave very differently on price,
 * lead time and contract weight, so the type drives qualification defaults.
 */
export const SUPPLIER_TYPES = [
  'hyperscaler', // AWS, Azure, GCP, OCI — deep capacity, rigid terms
  'neocloud', // GPU-first clouds — the core supply base
  'regional_operator', // Sovereign / in-country operators; jurisdiction matters
  'datacenter', // Colocation and bare-metal facility owners
  'broker', // Resellers and capacity brokers; margin stacks
  'depin', // Decentralised / crypto-adjacent compute networks
  'community', // Long-tail individual GPU owners
] as const;
export type SupplierType = (typeof SUPPLIER_TYPES)[number];

/**
 * Demand-side segments. Buying behaviour differs sharply: frontier labs
 * negotiate multi-year reserved capacity, applied startups burst on-demand,
 * and sovereign buyers bring procurement processes measured in quarters.
 */
export const CUSTOMER_SEGMENTS = [
  'frontier_lab',
  'applied_ai_startup',
  'enterprise',
  'research_institution',
  'sovereign',
  'individual_developer',
] as const;
export type CustomerSegment = (typeof CUSTOMER_SEGMENTS)[number];

// ---------------------------------------------------------------------------
// Product lines
// ---------------------------------------------------------------------------

/**
 * A single account can carry several independent opportunities across product
 * lines. Modelling every deal as "GPU hours" collapses that and makes
 * land-and-expand invisible, which is precisely the motion these businesses
 * run.
 */
export const PRODUCT_LINES = [
  'compute_ondemand',
  'compute_reserved',
  'compute_spot',
  'post_training', // RL / fine-tuning / hosted training
  'inference',
  'evaluations',
  'platform', // Hosted tooling subscriptions
] as const;
export type ProductLine = (typeof PRODUCT_LINES)[number];

// ---------------------------------------------------------------------------
// Pipelines
// ---------------------------------------------------------------------------

/**
 * Demand pipeline.
 *
 * Sourced from how enterprise AI-infrastructure deals are publicly described as
 * progressing: qualification, legal, scoping, proposal, procurement, POC,
 * deployment, expansion.
 *
 * Note that `legal` sits SECOND. In this market MSA and DPA execution gates the
 * engagement rather than concluding it — a customer will not hand workloads to
 * an infrastructure provider before paper is in place. Generic CRMs place
 * contracting at the end of the funnel and are simply wrong about it here.
 */
export const DEMAND_STAGES = [
  'qualification',
  'legal',
  'scoping',
  'proposal',
  'procurement',
  'poc',
  'deployment',
  'expansion',
  'closed_won',
  'closed_lost',
] as const;
export type DemandStage = (typeof DEMAND_STAGES)[number];

export const DEMAND_STAGE_LABELS: Record<DemandStage, string> = {
  qualification: 'Qualification',
  legal: 'Legal',
  scoping: 'Scoping',
  proposal: 'Proposal',
  procurement: 'Procurement',
  poc: 'POC',
  deployment: 'Deployment',
  expansion: 'Expansion',
  closed_won: 'Closed won',
  closed_lost: 'Closed lost',
};

/** Stages that represent a live, forecastable opportunity. */
export const DEMAND_OPEN_STAGES: readonly DemandStage[] = [
  'qualification',
  'legal',
  'scoping',
  'proposal',
  'procurement',
  'poc',
  'deployment',
  'expansion',
];

/**
 * Supply pipeline.
 *
 * Qualification is split into `technical_diligence` and `financial_diligence`
 * on purpose. Accepting capacity is a two-key decision — engineering judges
 * whether the cluster can actually train (interconnect, storage, reliability
 * history) while finance judges whether the economics clear. Collapsing them
 * into one "qualified" stage loses the record of who accepted what and why.
 */
export const SUPPLY_STAGES = [
  'sourced',
  'qualifying',
  'technical_diligence',
  'financial_diligence',
  'pricing',
  'contracting',
  'onboarding',
  'live',
  'renewal',
  'churned',
  'rejected',
] as const;
export type SupplyStage = (typeof SUPPLY_STAGES)[number];

export const SUPPLY_STAGE_LABELS: Record<SupplyStage, string> = {
  sourced: 'Sourced',
  qualifying: 'Qualifying',
  technical_diligence: 'Technical diligence',
  financial_diligence: 'Financial diligence',
  pricing: 'Pricing',
  contracting: 'Contracting',
  onboarding: 'Onboarding',
  live: 'Live',
  renewal: 'Renewal',
  churned: 'Churned',
  rejected: 'Rejected',
};

export const SUPPLY_OPEN_STAGES: readonly SupplyStage[] = [
  'sourced',
  'qualifying',
  'technical_diligence',
  'financial_diligence',
  'pricing',
  'contracting',
  'onboarding',
];

// ---------------------------------------------------------------------------
// Hardware
// ---------------------------------------------------------------------------

/**
 * GPU model identifiers follow the Prime Intellect availability API's
 * convention (`H100_80GB`, `B200`, …) so that synced inventory maps across
 * without translation. The list is open — unknown values are stored verbatim
 * rather than rejected, because new accelerators appear faster than schemas
 * are updated.
 */
export const KNOWN_GPU_TYPES = [
  'GB200',
  'B300',
  'B200',
  'H200',
  'H100_80GB',
  'GH200',
  'A100_80GB',
  'A100_40GB',
  'L40S',
  'A40',
  'RTX_4090',
  'RTX_5090',
] as const;
export type KnownGpuType = (typeof KNOWN_GPU_TYPES)[number];

/** Socket type. SXM implies NVLink and therefore multi-node training viability. */
export const GPU_SOCKETS = ['PCIe', 'SXM2', 'SXM3', 'SXM4', 'SXM5', 'SXM6'] as const;
export type GpuSocket = (typeof GPU_SOCKETS)[number];

/**
 * Interconnect is the field that decides whether a cluster can train or can
 * only serve. Ethernet-only capacity at a training price is the most common way
 * to be overcharged in this market.
 */
export const INTERCONNECT_TYPES = [
  'Infiniband',
  'RoCE',
  'NVLink',
  'Ethernet',
  'Unknown',
] as const;
export type InterconnectType = (typeof INTERCONNECT_TYPES)[number];

/**
 * Reliability tier.
 *
 * This is how compute aggregators express reliability when they cannot offer a
 * conventional uptime SLA — because they resell third-party infrastructure they
 * do not control. Government capacity is sovereign-grade and isolated,
 * secure-cloud capacity sits in vetted datacenters, and community capacity is
 * cheaper and less dependable. See `SlaKind` below for how this interacts with
 * contractual commitments.
 */
export const SECURITY_TIERS = ['government', 'secure_cloud', 'community_cloud'] as const;
export type SecurityTier = (typeof SECURITY_TIERS)[number];

/** Allocation states distinguish forecast, sold usage, and returned capacity. */
export const CONSUMING_ALLOCATION_STATUSES = ['committed', 'active', 'completed'] as const;
export const RESERVING_ALLOCATION_STATUSES = [
  'planned',
  ...CONSUMING_ALLOCATION_STATUSES,
] as const;
export const ALLOCATION_STATUSES = [
  ...RESERVING_ALLOCATION_STATUSES,
  'released',
] as const;
export type AllocationStatus = (typeof ALLOCATION_STATUSES)[number];

/** Commercial priority is separate from lifecycle state. */
export const GUARANTEE_TYPES = [
  'guaranteed',
  'committed',
  'on_demand',
  'preemptible',
  'internal',
] as const;
export type GuaranteeType = (typeof GUARANTEE_TYPES)[number];

/** Higher classifications may satisfy lower requirements, never the reverse. */
export const SECURITY_TIER_RANK: Record<SecurityTier, number> = {
  community_cloud: 0,
  secure_cloud: 1,
  government: 2,
};

export function securityTierSatisfies(
  available: SecurityTier,
  required: SecurityTier,
): boolean {
  return SECURITY_TIER_RANK[available] >= SECURITY_TIER_RANK[required];
}

/** Scarcity signal on a listing. Drives "sell this now" alerts. */
export const STOCK_STATUSES = [
  'Available',
  'Low',
  'Medium',
  'High',
  'Unavailable',
] as const;
export type StockStatus = (typeof STOCK_STATUSES)[number];

// ---------------------------------------------------------------------------
// Contracts
// ---------------------------------------------------------------------------

export const CONTRACT_TYPES = [
  'msa', // Master Service Agreement — the umbrella
  'dpa', // Data Processing Agreement — required for most enterprise buyers
  'sla', // Service Level Agreement — see SLA_KINDS
  'order_form', // The commercial specifics under an MSA
  'capacity_commitment', // Reserved capacity purchased from a supplier
  'nda',
  'amendment',
] as const;
export type ContractType = (typeof CONTRACT_TYPES)[number];

export const CONTRACT_STATUSES = [
  'draft',
  'in_review',
  'in_negotiation',
  'out_for_signature',
  'executed',
  'expired',
  'terminated',
] as const;
export type ContractStatus = (typeof CONTRACT_STATUSES)[number];

/**
 * How service levels are actually promised in this market.
 *
 * A compute aggregator generally CANNOT offer a conventional uptime guarantee
 * on resold capacity, and says so publicly. What it offers instead is a
 * reliability tier plus a credits policy. But the same company will negotiate
 * heavyweight SLAs on the supply side — reserved capacity agreements, MSAs,
 * DPAs and order forms at very large scale — and will negotiate bespoke SLAs
 * with enterprise customers on dedicated clusters.
 *
 * So all three shapes coexist and the schema must hold them without pretending
 * one is the others:
 *
 *   `none`            Self-serve. No service commitment at all.
 *   `credits_policy`  Reliability tier + service credits on provider failure.
 *   `negotiated`      A real, signed SLA with committed metrics.
 */
export const SLA_KINDS = ['none', 'credits_policy', 'negotiated'] as const;
export type SlaKind = (typeof SLA_KINDS)[number];

/** Committed metrics on a negotiated SLA. */
export const SLA_METRICS = [
  'uptime_pct',
  'node_availability_pct',
  'node_replacement_hours',
  'mttr_hours',
  'support_response_hours',
  'throughput_floor',
] as const;
export type SlaMetric = (typeof SLA_METRICS)[number];

// ---------------------------------------------------------------------------
// Evidence
// ---------------------------------------------------------------------------

/**
 * Every agent-derived fact carries a confidence band. Strong signals apply
 * automatically; weak ones become proposals for a human to accept or dismiss.
 *
 * This is what separates a trustworthy CRM from a store of plausible-sounding
 * fabrications. An agent that can write unattributed claims into the record
 * will eventually write a wrong one, and nobody will be able to tell which.
 */
export const FACT_BANDS = ['verified', 'probable', 'possible'] as const;
export type FactBand = (typeof FACT_BANDS)[number];

export const FACT_STATUSES = [
  'applied', // Written to the record
  'approved', // Accepted by a person, but not written without a field-aware applicator
  'proposed', // Awaiting human review
  'dismissed', // Rejected by a human
  'superseded', // Replaced by a newer fact
] as const;
export type FactStatus = (typeof FACT_STATUSES)[number];

/** Confidence thresholds governing whether a fact applies or is merely proposed. */
export const FACT_BAND_THRESHOLDS: Record<FactBand, number> = {
  verified: 0.9,
  probable: 0.65,
  possible: 0,
};

export function bandForScore(score: number): FactBand {
  if (score >= FACT_BAND_THRESHOLDS.verified) return 'verified';
  if (score >= FACT_BAND_THRESHOLDS.probable) return 'probable';
  return 'possible';
}

/** Only `verified` facts write themselves. Everything else asks first. */
export function statusForBand(band: FactBand): FactStatus {
  return band === 'verified' ? 'applied' : 'proposed';
}

// ---------------------------------------------------------------------------
// Activities
// ---------------------------------------------------------------------------

export const ACTIVITY_TYPES = [
  'note',
  'email',
  'call',
  'meeting',
  'slack',
  'buzz',
  'stage_change',
  'contract_event',
  'task',
  'agent_action',
] as const;
export type ActivityType = (typeof ACTIVITY_TYPES)[number];

/** Outbound events a linked collaboration channel may subscribe to. */
export const NOTIFICATION_KINDS = ['stage_change', 'idle_capacity'] as const;
export type NotificationKind = (typeof NOTIFICATION_KINDS)[number];

// ---------------------------------------------------------------------------
// Agent task queue
// ---------------------------------------------------------------------------

/**
 * Work Piggy can be asked to do. The API writes rows of these; it never calls
 * the agent directly. The queue therefore survives the agent being offline, and
 * restarting the agent replays nothing that already finished.
 */
export const AGENT_TASK_KINDS = [
  'enrich_account',
  'enrich_contact',
  'write_brief',
  'match_capacity',
  'detect_idle_capacity',
  'summarise_pipeline',
  'watch_renewal',
  'research_supplier',
] as const;
export type AgentTaskKind = (typeof AGENT_TASK_KINDS)[number];

export const AGENT_TASK_OUTCOMES = [
  'succeeded',
  'failed',
  'skipped',
  'cancelled',
] as const;
export type AgentTaskOutcome = (typeof AGENT_TASK_OUTCOMES)[number];

// ---------------------------------------------------------------------------
// Provenance
// ---------------------------------------------------------------------------

/**
 * How a record entered PIG. Seeded records are public research and are graded,
 * never presented as established fact.
 */
export const RECORD_SOURCES = [
  'manual',
  'seed',
  'agent',
  'prime_api',
  'slack',
  'buzz',
  'import',
] as const;
export type RecordSource = (typeof RECORD_SOURCES)[number];

/**
 * Confidence in a seeded or enriched record about a real person or company.
 *
 * `unverified` is a first-class value and must be shown in the UI. Presenting
 * a single-source claim about a real person with the same visual weight as a
 * corroborated one is how a CRM quietly becomes misinformation.
 */
export const CONFIDENCE_GRADES = [
  'confirmed', // Two or more independent sources
  'probable', // One good source
  'unverified', // Single weak or self-reported source
  'disputed', // Sources conflict
] as const;
export type ConfidenceGrade = (typeof CONFIDENCE_GRADES)[number];

/**
 * Relationship of a person to the organisation they are filed under. Being
 * named in a company's repository or paper does not make someone an employee,
 * and a CRM that conflates the two will embarrass whoever acts on it.
 */
export const AFFILIATION_KINDS = [
  'staff',
  'founder',
  'alumni',
  'contributor', // Open-source contributor, not employed
  'resident', // Residency or internship programme
  'advisor',
  'investor',
  'customer_reference',
  'unknown',
] as const;
export type AffiliationKind = (typeof AFFILIATION_KINDS)[number];
