// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import { ObjectSchema, Field } from '@objectstack/spec/data';
import { cel, P } from '@objectstack/spec';

/**
 * Project — the realistic backbone object. Demonstrates lookup relations,
 * a formula field, a select that drives Kanban grouping, date fields that
 * drive Gantt/timeline views, validations, and a status state machine.
 */
export const Project = ObjectSchema.create({
  name: 'showcase_project',
  label: 'Project',
  pluralLabel: 'Projects',
  icon: 'folder-kanban',
  description: 'A delivery project for an account.',

  fields: {
    name: Field.text({ label: 'Project Name', required: true, searchable: true, maxLength: 200 }),
    // `relatedList*` is the read-side mirror of inline editing: the Account's
    // record DETAIL page auto-renders a "Projects" related list — derived from
    // this lookup relationship, with no page config. Title and columns are
    // declared here on the relationship (where AI authors the model), not in a
    // hand-built page.
    account: Field.lookup('showcase_account', {
      label: 'Account',
      required: true,
      relatedListTitle: 'Projects',
      relatedListColumns: ['name', 'status', 'health', 'budget', 'end_date'],
    }),
    status: Field.select({
      label: 'Status',
      required: true,
      options: [
        { label: 'Planned', value: 'planned', default: true, color: '#94A3B8' },
        { label: 'Active', value: 'active', color: '#3B82F6' },
        { label: 'On Hold', value: 'on_hold', color: '#F59E0B' },
        { label: 'Completed', value: 'completed', color: '#10B981' },
        { label: 'Cancelled', value: 'cancelled', color: '#EF4444' },
      ],
    }),
    health: Field.select({
      label: 'Health',
      options: [
        { label: 'Green', value: 'green', default: true, color: '#10B981' },
        { label: 'Yellow', value: 'yellow', color: '#F59E0B' },
        { label: 'Red', value: 'red', color: '#EF4444' },
      ],
    }),
    budget: Field.currency({ label: 'Budget', scale: 2, min: 0 }),
    spent: Field.currency({ label: 'Spent', scale: 2, min: 0, defaultValue: 0 }),
    budget_remaining: Field.formula({
      label: 'Budget Remaining',
      expression: cel`(budget == null ? 0 : budget) - (spent == null ? 0 : spent)`,
    }),
    start_date: Field.date({ label: 'Start Date' }),
    end_date: Field.date({ label: 'Target End Date' }),
    owner: Field.text({ label: 'Owner', maxLength: 200 }),
    // Roll-up summaries — recomputed server-side whenever a child task is
    // inserted / updated / deleted (FK auto-detected: showcase_task.project).
    task_count: Field.summary({
      label: 'Tasks',
      summaryOperations: { object: 'showcase_task', field: 'estimate_hours', function: 'count' },
    }),
    total_estimate: Field.summary({
      label: 'Total Estimate (h)',
      summaryOperations: { object: 'showcase_task', field: 'estimate_hours', function: 'sum' },
    }),
  },

  validations: [
    {
      type: 'cross_field' as const,
      name: 'end_after_start',
      label: 'End After Start',
      description: 'Target end date must be on or after the start date.',
      fields: ['start_date', 'end_date'],
      condition: P`has(record.start_date) && has(record.end_date) && record.end_date < record.start_date`,
      message: 'Target End Date must be on or after the Start Date.',
    },
    {
      type: 'script' as const,
      name: 'spent_within_budget',
      label: 'Spend Within 120% of Budget',
      description: 'Flag projects spending more than 120% of budget.',
      condition: P`record.budget != null && record.spent != null && record.spent > record.budget * 1.2`,
      message: 'Spend exceeds 120% of budget — escalate before continuing.',
      severity: 'error' as const,
    },
    {
      type: 'state_machine' as const,
      name: 'project_status_flow',
      label: 'Project Status Flow',
      description: 'Projects progress through valid status transitions.',
      field: 'status',
      // State machines validate *transitions*, so they run on update only —
      // an insert sets the initial state (constrained by the select options).
      events: ['update'] as const,
      message: 'Invalid project status transition.',
      transitions: {
        planned: ['active', 'cancelled'],
        active: ['on_hold', 'completed', 'cancelled'],
        on_hold: ['active', 'cancelled'],
        completed: [],
        cancelled: ['planned'],
      },
    },
    {
      // Advisory (severity: 'warning') state machine — demonstrates a soft
      // transition guard: health should escalate/de-escalate one step at a
      // time (green↔yellow↔red). A jump (e.g. green→red) is *flagged* (logged
      // server-side) but NOT blocked, unlike the error-severity status flow.
      type: 'state_machine' as const,
      name: 'project_health_progression',
      label: 'Project Health Progression (advisory)',
      description: 'Health should change one step at a time; skips are warned, not blocked.',
      field: 'health',
      events: ['update'] as const,
      severity: 'warning' as const,
      message: 'Health changed by more than one step — confirm this is intentional.',
      transitions: {
        green: ['yellow'],
        yellow: ['green', 'red'],
        red: ['yellow'],
      },
    },
  ],
});
