import { Form, Head, Link, router } from '@inertiajs/react';
import { useMemo, useState } from 'react';
import {
    AlertTriangle,
    ArrowRight,
    CalendarClock,
    CheckCircle2,
    ClipboardCheck,
    Clock,
    DollarSign,
    FileUp,
    History,
    House,
    Info,
    LifeBuoy,
    Package,
    Pencil,
    PlugZap,
    Plus,
    ShieldAlert,
    ShieldCheck,
    Sun,
    Trash2,
    Users,
    Workflow,
    Zap,
} from 'lucide-react';
import JobActivityController from '@/actions/App/Http/Controllers/JobActivityController';
import JobStatusController from '@/actions/App/Http/Controllers/JobStatusController';
import { FormField, NativeSelect } from '@/components/crm/native-select';
import {
    Callout,
    DetailRow,
    EmptyState,
    KeyValueGrid,
    PageHeader,
    SectionCard,
} from '@/components/crm/page-header';
import { StageRail } from '@/components/crm/pipeline-board';
import { RichText } from '@/components/crm/rich-text';
import { SectionNav } from '@/components/crm/section-nav';
import { OwnershipHistory } from '@/components/crm/ownership-history';
import { TransferOwnershipDialog } from '@/components/crm/transfer-ownership-dialog';
import { StatStrip } from '@/components/crm/kpi-tile';
import {
    GridStatusBadge,
    InspectionBadge,
    JobStatusBadge,
    PriorityBadge,
    RuleFlag,
    StatusDot,
    StatusLabel,
    StatusPill,
    StcBadge,
} from '@/components/crm/status-badge';
import { ActivityTimeline, StatusAuditTrail } from '@/components/crm/timeline';
import { toneClasses } from '@/components/crm/tone';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import {
    Table,
    TableBody,
    TableCell,
    TableHead,
    TableHeader,
    TableRow,
} from '@/components/ui/table';
import { useRelease } from '@/hooks/use-release';
import {
    formatCurrency,
    formatDate,
    formatDateTime,
    formatDuration,
    formatKilowatts,
    formatNumber,
} from '@/lib/format';
import { cn } from '@/lib/utils';
import { show as customerShow } from '@/routes/customers';
import {
    destroy as jobDestroy,
    edit as jobEdit,
    index as jobsIndex,
} from '@/routes/jobs';
import {
    create as ticketsCreate,
    show as ticketShow,
} from '@/routes/tickets';
import type {
    ActivityEntry,
    ComplianceContext,
    DocumentSummary,
    InspectionSummary,
    JobDetail,
    Option,
    OptionsBundle,
    StatusHistoryEntry,
    StatusOptionGroup,
    TicketCounts,
    TicketSummary,
    WorkflowOption,
} from '@/types';

type PageProps = {
    job: JobDetail;
    workflow: { options: WorkflowOption[]; permitted: string[] };
    statusHistory: StatusHistoryEntry[];
    activities: ActivityEntry[];
    documents: DocumentSummary[];
    tickets: TicketSummary[];
    ticketCounts: TicketCounts;
    inspections: InspectionSummary[];
    outstandingDocuments: Option[];
    complianceContext: ComplianceContext | null;
    options: OptionsBundle;
    can: {
        update: boolean;
        delete: boolean;
        assign: boolean;
        viewFinancials: boolean;
        viewCompliance: boolean;
        manageCompliance: boolean;
        uploadDocuments: boolean;
        createTicket: boolean;
        requestInspection: boolean;
        transferOwnership: boolean;
    };
};

type Stage = {
    value: string;
    label: string;
    color: string;
    order: number;
    waiting_on: string;
    group: string;
    group_label: string;
};

const textareaClassName =
    'border-input dark:bg-input/30 flex min-h-[84px] w-full rounded-md border bg-transparent px-3 py-2 text-sm shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50';

/**
 * Milestone dates, in the order they happen on an Australian solar job.
 */
const milestoneFields: Array<{ key: string; label: string; dateOnly?: boolean }> = [
    { key: 'sold_on', label: 'Sold', dateOnly: true },
    { key: 'site_inspected_at', label: 'Site inspected' },
    { key: 'design_approved_at', label: 'Design approved' },
    { key: 'stock_picked_at', label: 'Stock picked' },
    { key: 'dispatched_at', label: 'Dispatched to installers' },
    { key: 'install_scheduled_for', label: 'Installation booked for', dateOnly: true },
    { key: 'installation_started_at', label: 'Installation started' },
    { key: 'installation_completed_at', label: 'Installation completed' },
    { key: 'inspection_booked_for', label: 'Inspection booked for' },
    { key: 'inspection_passed_at', label: 'Electrical inspection passed' },
    { key: 'grid_connected_at', label: 'Grid connection closed out' },
    { key: 'stc_submitted_at', label: 'STC claim submitted' },
    { key: 'completed_at', label: 'Job completed' },
    { key: 'cancelled_at', label: 'Cancelled' },
];

/**
 * The whole pipeline, flattened from the grouped status options the controller
 * already sends, so this page can show where the job sits without another
 * request or a new prop.
 */
function flattenStages(groups: StatusOptionGroup[]): Stage[] {
    return groups.flatMap((group) =>
        group.statuses.map((status) => ({
            value: status.value,
            label: status.label,
            color: status.color,
            order: status.order,
            waiting_on: status.waiting_on,
            group: group.group,
            group_label: group.group_label,
        })),
    );
}

/**
 * A blocked stage in the compliance or closeout phase is a hard stop — the
 * system cannot be energised or the job closed. Everything else is a step that
 * is simply not ready yet.
 */
function blockerTone(option: WorkflowOption): string {
    return option.group === 'compliance' || option.group === 'closed'
        ? 'danger'
        : 'warning';
}

export default function JobShow({
    job,
    workflow,
    statusHistory,
    activities,
    documents,
    tickets,
    ticketCounts,
    inspections,
    outstandingDocuments,
    complianceContext,
    options,
    can,
}: PageProps) {
    const handleDelete = () => {
        if (window.confirm(`Archive job ${job.job_number}? The job history is kept.`)) {
            router.delete(jobDestroy({ job: job.id }));
        }
    };

    // Inspections and support tickets are parts of their own, so a job page
    // does not carry a panel or a section link for one that is not released.
    const { release } = useRelease();
    const inspectionsReleased =
        release.parts.inspections?.enabled ?? true;
    const ticketsReleased = release.parts.tickets?.enabled ?? true;

    const stages = useMemo(() => flattenStages(options.jobStatuses), [options.jobStatuses]);

    const sectionLinks = useMemo(() => {
        const links = [
            { id: 'workflow', label: 'Workflow' },
            { id: 'blocking', label: 'Blockers' },
        ];

        if (complianceContext) {
            links.push({ id: 'compliance', label: 'Compliance' });
        }

        if (job.grid) {
            links.push({ id: 'grid', label: 'Grid connection' });
        }

        if (job.inspection) {
            links.push({ id: 'inspection', label: 'Inspection' });
        }

        if (job.stc) {
            links.push({ id: 'stc', label: 'STC claim' });
        }

        if (job.solar_victoria?.applies) {
            links.push({ id: 'victoria', label: 'Rebate program' });
        }

        links.push(
            { id: 'system', label: 'System' },
            { id: 'dates', label: 'Dates' },
            { id: 'assignments', label: 'Assignments' },
            { id: 'owners', label: 'Owners' },
        );

        if (job.readiness.lines.length > 0) {
            links.push({ id: 'readiness', label: 'Stock' });
        }

        if (job.parts.length > 0) {
            links.push({ id: 'parts', label: 'Parts' });
        }

        if (job.financials) {
            links.push({ id: 'financials', label: 'Financials' });
        }

        links.push({ id: 'documents', label: 'Documents' });

        if (inspectionsReleased) {
            links.push({ id: 'inspections', label: 'Inspections' });
        }

        if (ticketsReleased) {
            links.push({ id: 'tickets', label: 'Support tickets' });
        }

        links.push(
            { id: 'activity', label: 'Activity' },
            { id: 'history', label: 'Audit trail' },
        );

        return links;
    }, [complianceContext, job.grid, job.inspection, job.stc, job.solar_victoria, job.financials, job.parts.length, inspectionsReleased, ticketsReleased]);

    return (
        <>
            <Head title={`Job ${job.job_number}`} />

            <div className="flex flex-col gap-5">
                <PageHeader
                    eyebrow={
                        <>
                            <StatusPill tone={job.status.color}>{job.status.label}</StatusPill>
                            {job.customer ? (
                                <>
                                    <span aria-hidden className="text-muted-foreground/40">
                                        ·
                                    </span>
                                    <Link
                                        href={customerShow({ customer: job.customer.id })}
                                        className="hover:text-foreground underline-offset-4 hover:underline"
                                    >
                                        {job.customer.name}
                                    </Link>
                                </>
                            ) : null}
                            {job.site ? (
                                <>
                                    <span aria-hidden className="text-muted-foreground/40">
                                        ·
                                    </span>
                                    <span>{job.site.address}</span>
                                    {job.site.state_label ? (
                                        <span>
                                            {job.site.state_label}
                                            {job.site.distribution_network
                                                ? ` · ${job.site.distribution_network}`
                                                : ''}
                                        </span>
                                    ) : null}
                                </>
                            ) : null}
                        </>
                    }
                    title={`${job.job_number} · ${job.title ?? job.type.label}`}
                    description={job.status.description}
                    actions={
                        <>
                            {can.update ? (
                                <Button variant="outline" size="sm" asChild>
                                    <Link href={jobEdit({ job: job.id })}>
                                        <Pencil />
                                        Edit job
                                    </Link>
                                </Button>
                            ) : null}
                            {can.delete ? (
                                <Button
                                    variant="outline"
                                    size="sm"
                                    onClick={handleDelete}
                                    data-test="delete-job-button"
                                >
                                    <Trash2 />
                                    Archive
                                </Button>
                            ) : null}
                        </>
                    }
                />

                <JobSummaryCard
                    job={job}
                    stages={stages}
                    canViewFinancials={can.viewFinancials}
                />

                <div className="bg-background/85 sticky top-14 z-10 -mx-1 rounded-lg px-1 py-1.5 backdrop-blur">
                    <SectionNav sections={sectionLinks} />
                </div>

                <WorkflowPanel
                    job={job}
                    options={workflow.options}
                    permitted={workflow.permitted}
                />

                <BlockersPanel
                    job={job}
                    options={workflow.options}
                    outstandingDocuments={outstandingDocuments}
                />

                {complianceContext ? (
                    <ComplianceOverview context={complianceContext} job={job} />
                ) : null}

                <div className="grid gap-4 xl:grid-cols-2">
                    {job.grid ? <GridPanel job={job} /> : null}
                    {job.inspection ? (
                        <InspectionPanel job={job} context={complianceContext} />
                    ) : null}
                    {job.stc ? <StcPanel job={job} /> : null}
                    {job.solar_victoria?.applies ? (
                        <SolarVictoriaPanel job={job} context={complianceContext} />
                    ) : null}
                </div>

                <div className="grid gap-4 xl:grid-cols-2">
                    <SystemPanel job={job} />
                    <div className="flex flex-col gap-4">
                        <DatesPanel job={job} />
                        <AssignmentsPanel job={job} />
                        {job.financials ? <FinancialsPanel job={job} /> : null}
                    </div>
                </div>

                <OwnersPanel job={job} can={can} options={options} />

                <ReadinessPanel job={job} />

                <PartsPanel job={job} />

                <DocumentsPanel
                    documents={documents}
                    outstanding={outstandingDocuments}
                    canUpload={can.uploadDocuments}
                />

                {inspectionsReleased ? (
                    <InspectionsPanel
                        inspections={inspections}
                        canRequest={can.requestInspection}
                        jobId={job.id}
                    />
                ) : null}

                {ticketsReleased ? (
                    <TicketsPanel
                        tickets={tickets}
                        counts={ticketCounts}
                        canCreate={can.createTicket}
                        jobId={job.id}
                    />
                ) : null}

                <div className="grid gap-4 xl:grid-cols-2">
                    <ActivityPanel
                        job={job}
                        activityTypes={options.activityTypes}
                        activities={activities}
                    />
                    <StatusHistoryPanel history={statusHistory} />
                </div>
            </div>
        </>
    );
}

/* ------------------------------------------------------------------ header */

/**
 * The job at a glance: the figures that matter, where it sits in the
 * installation process, and who and where it is for.
 */
function JobSummaryCard({
    job,
    stages,
    canViewFinancials,
}: {
    job: JobDetail;
    stages: Stage[];
    canViewFinancials: boolean;
}) {
    const strip = [
        {
            label: 'Time in stage',
            value: formatDuration(job.days_in_status),
            tone: job.is_stalled ? 'warning' : 'slate',
            icon: Clock,
        },
        {
            label: 'System size',
            value: formatKilowatts(job.system.system_size_kw),
            tone: 'solar',
            icon: Sun,
        },
        {
            label: 'Progress',
            value: `${job.progress}%`,
            tone: job.status.is_cancelled ? 'danger' : 'success',
            icon: CheckCircle2,
        },
    ];

    if (canViewFinancials) {
        strip.push({
            label: 'Contract value',
            value: formatCurrency(job.contract_value),
            tone: 'info',
            icon: DollarSign,
        });
    }

    return (
        <section className="space-y-4">
            <StatStrip
                items={strip}
                className={strip.length === 3 ? 'sm:grid-cols-3' : undefined}
            />

            <div className="bg-card shadow-card grid gap-6 rounded-xl p-5 ring-1 ring-hairline lg:grid-cols-[1.15fr_1fr]">
                <div className="min-w-0 space-y-4">
                    <div className="flex flex-wrap items-center gap-2">
                        <JobStatusBadge status={job.status} variant="pill" />
                        <PriorityBadge priority={job.priority} />
                        <StatusPill tone="slate">{job.type.label}</StatusPill>
                        {job.site?.state ? (
                            <StatusPill tone="slate">{job.site.state}</StatusPill>
                        ) : null}
                        {job.requires_inspection ? (
                            <RuleFlag tone="info">Inspection required</RuleFlag>
                        ) : null}
                        {job.is_stalled ? (
                            <RuleFlag tone="warning">Stalled</RuleFlag>
                        ) : null}
                    </div>

                    <StageRail
                        stages={stages}
                        currentStatus={job.status.value}
                        progress={job.progress}
                    />
                </div>

                <dl className="grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-2">
                    <DetailRow
                        label="Customer"
                        value={
                            job.customer ? (
                                <Link
                                    href={customerShow({ customer: job.customer.id })}
                                    className="font-medium underline-offset-4 hover:underline"
                                >
                                    {job.customer.name}
                                </Link>
                            ) : null
                        }
                    />
                    <DetailRow
                        label="Time in status"
                        value={
                            <>
                                {formatDuration(job.days_in_status)}
                                <span className="text-muted-foreground block text-xs">
                                    since {formatDate(job.status_changed_at)}
                                </span>
                            </>
                        }
                    />
                    <DetailRow
                        label="Installation site"
                        value={
                            job.site ? (
                                <>
                                    {job.site.address}
                                    <span className="text-muted-foreground block text-xs">
                                        {job.site.suburb} {job.site.postcode}
                                        {job.site.distribution_network
                                            ? ` · ${job.site.distribution_network}`
                                            : ''}
                                    </span>
                                </>
                            ) : null
                        }
                    />
                    <DetailRow
                        label="NMI"
                        value={
                            job.site?.nmi ? (
                                <span className="crm-num font-mono text-xs">
                                    {job.site.nmi}
                                </span>
                            ) : null
                        }
                    />
                </dl>
            </div>
        </section>
    );
}

/* ---------------------------------------------------------------- workflow */

function WorkflowPanel({
    job,
    options,
    permitted,
}: {
    job: JobDetail;
    options: WorkflowOption[];
    permitted: string[];
}) {
    const firstAllowed =
        options.find((option) => option.is_next && option.allowed)?.value ??
        options.find((option) => option.allowed)?.value ??
        null;

    const [selected, setSelected] = useState<string | null>(firstAllowed);
    const selectedOption = options.find((option) => option.value === selected) ?? null;

    return (
        <SectionCard
            id="workflow"
            title="Installation workflow"
            description="Move the job to its next stage. Stages the business or the network does not allow yet are disabled with the reason."
            icon={Workflow}
            tone="solar"
        >
            {options.length === 0 ? (
                <EmptyState
                    icon={CheckCircle2}
                    title="This job is closed"
                    description="A completed or cancelled job cannot move any further along the pipeline."
                />
            ) : (
                <Form
                    {...JobStatusController.store.form({ job: job.id })}
                    options={{ preserveScroll: true }}
                >
                    {({ processing, errors }) => (
                        <div className="space-y-4">
                            <input type="hidden" name="status" value={selected ?? ''} />

                            <div className="grid gap-2 sm:grid-cols-2 xl:grid-cols-3">
                                {options.map((option) => {
                                    const isSelected = selected === option.value;
                                    const classes = toneClasses(blockerTone(option));

                                    return (
                                        <button
                                            type="button"
                                            key={option.value}
                                            disabled={!option.allowed}
                                            aria-pressed={isSelected}
                                            onClick={() => setSelected(option.value)}
                                            className={cn(
                                                'rounded-lg p-3 text-left ring-1 ring-inset transition',
                                                'focus-visible:ring-ring/50 outline-none focus-visible:ring-[3px]',
                                                option.allowed
                                                    ? 'cursor-pointer hover:ring-foreground/20'
                                                    : 'cursor-not-allowed',
                                                'ring-hairline bg-surface/50',
                                                isSelected && option.allowed
                                                    ? 'bg-accent ring-solar/50'
                                                    : null,
                                                !option.allowed && 'opacity-70',
                                            )}
                                        >
                                            <span className="flex items-center gap-2 text-sm font-medium">
                                                {option.allowed ? (
                                                    <CheckCircle2 className="text-success size-4 shrink-0" />
                                                ) : (
                                                    <AlertTriangle
                                                        className={cn(
                                                            'size-4 shrink-0',
                                                            toneClasses(
                                                                blockerTone(option),
                                                            ).text,
                                                        )}
                                                    />
                                                )}
                                                <span className="min-w-0 truncate">
                                                    {option.label}
                                                </span>
                                                {option.is_next ? (
                                                    <StatusPill
                                                        tone="solar"
                                                        className="ml-auto text-[10px]"
                                                    >
                                                        Next
                                                    </StatusPill>
                                                ) : null}
                                            </span>
                                            <span className="crm-label mt-1.5 block">
                                                {option.group_label}
                                            </span>
                                            {!option.allowed && option.reason ? (
                                                <span
                                                    className={cn(
                                                        'mt-2 block text-xs leading-relaxed',
                                                        classes.text,
                                                    )}
                                                >
                                                    {option.reason}
                                                </span>
                                            ) : null}
                                        </button>
                                    );
                                })}
                            </div>

                            {selectedOption?.requires_note ? (
                                <FormField
                                    label="Reason for this change"
                                    htmlFor="status-notes"
                                    required
                                    error={errors.notes}
                                    hint="This is recorded permanently against the job."
                                >
                                    <textarea
                                        id="status-notes"
                                        name="notes"
                                        rows={3}
                                        required
                                        className={textareaClassName}
                                    />
                                </FormField>
                            ) : null}

                            {errors.status ? (
                                <p className="text-destructive text-sm">{errors.status}</p>
                            ) : null}

                            {permitted.length === 0 ? (
                                <p className="text-muted-foreground flex items-center gap-2 text-xs">
                                    <Info className="size-3.5" />
                                    Your role cannot move this job to any of the available stages.
                                </p>
                            ) : null}

                            <div className="border-hairline flex items-center gap-3 border-t pt-4">
                                <Button
                                    type="submit"
                                    disabled={
                                        processing ||
                                        !selectedOption ||
                                        !selectedOption.allowed
                                    }
                                    data-test="transition-job-button"
                                >
                                    <ArrowRight />
                                    {selectedOption
                                        ? `Move to ${selectedOption.label}`
                                        : 'Select a stage'}
                                </Button>
                                {job.status.is_terminal ? (
                                    <span className="text-muted-foreground text-xs">
                                        Terminal status.
                                    </span>
                                ) : null}
                            </div>
                        </div>
                    )}
                </Form>
            )}
        </SectionCard>
    );
}

/* --------------------------------------------------------------- blockers */

function BlockersPanel({
    job,
    options,
    outstandingDocuments,
}: {
    job: JobDetail;
    options: WorkflowOption[];
    outstandingDocuments: Option[];
}) {
    const blocked = options.filter((option) => !option.allowed && option.reason);
    const nextStep = options.find((option) => option.is_next);
    const hasOpenWork = blocked.length > 0 || outstandingDocuments.length > 0;

    return (
        <SectionCard
            id="blocking"
            title="What is blocking progress"
            description="Generated from the compliance gates in the workflow engine and the evidence still missing from this job."
            icon={ShieldAlert}
            tone={hasOpenWork ? 'warning' : 'success'}
        >
            {!hasOpenWork ? (
                <Callout tone="success" icon={CheckCircle2} title="Nothing is blocking this job">
                    {nextStep ? (
                        <>
                            It can move on to <strong>{nextStep.label}</strong>.
                        </>
                    ) : (
                        'Every gate this job needs has been satisfied.'
                    )}
                </Callout>
            ) : (
                <div className="space-y-2.5">
                    {nextStep && !nextStep.allowed && nextStep.reason ? (
                        <Callout
                            tone={blockerTone(nextStep)}
                            icon={AlertTriangle}
                            title={`Next stage (${nextStep.label}) is blocked`}
                        >
                            {nextStep.reason}
                        </Callout>
                    ) : null}

                    {blocked
                        .filter((option) => option.value !== nextStep?.value)
                        .map((option) => (
                            <Callout
                                key={option.value}
                                tone={blockerTone(option)}
                                icon={AlertTriangle}
                                title={option.label}
                            >
                                {option.reason}
                            </Callout>
                        ))}

                    {outstandingDocuments.length > 0 ? (
                        <Callout tone="info" icon={Info} title="Evidence still missing">
                            {outstandingDocuments
                                .map((document) => document.label)
                                .join(', ')}
                            .
                        </Callout>
                    ) : null}
                </div>
            )}

            {job.status.is_terminal ? null : (
                <p className="text-muted-foreground mt-4 text-xs">
                    This job is waiting on{' '}
                    <span className="text-foreground font-medium">
                        {job.status.waiting_on}
                    </span>{' '}
                    since {formatDate(job.status_changed_at)}.
                </p>
            )}
        </SectionCard>
    );
}

/* -------------------------------------------------------------- compliance */

function ComplianceOverview({
    context,
    job,
}: {
    context: ComplianceContext;
    job: JobDetail;
}) {
    const auditProgram = job.solar_victoria?.audit_program ?? 'The state safety audit';

    return (
        <SectionCard
            id="compliance"
            title={`${context.state_label} compliance context`}
            description="Which rules apply to this installation, based on where the system is being installed."
            icon={ShieldCheck}
            tone="info"
        >
            <KeyValueGrid
                columns={4}
                items={[
                    {
                        label: 'Distribution network',
                        value:
                            context.dnsp ?? (
                                <span className="text-warning">
                                    Not identified — confirm the DNSP from the address
                                </span>
                            ),
                    },
                    {
                        label: 'Grid pre-approval',
                        value: context.requires_pre_approval
                            ? 'Required before installation'
                            : 'Not required',
                    },
                    {
                        label: 'Electrical certificate',
                        value: context.certificate_name,
                    },
                    {
                        label: 'State program',
                        value: context.rebate_program ?? 'No state rebate program tracked',
                    },
                ]}
            />

            <div className="mt-4 space-y-2.5">
                {context.requires_independent_inspection ? (
                    <Callout
                        tone="warning"
                        icon={ShieldCheck}
                        title={`${context.state_label} requires an independent electrical inspection`}
                    >
                        Solar PV is prescribed electrical installation work here, so{' '}
                        {context.inspection_authority ?? 'a Licensed Electrical Inspector'}{' '}
                        must inspect the completed system and a {context.certificate_name} must
                        be issued before the system can be energised. This job cannot be
                        completed or connected until that pass is recorded.
                    </Callout>
                ) : null}

                {context.has_sampled_safety_audit ? (
                    <Callout tone="info" icon={Info} title={`${auditProgram} is a sampled audit`}>
                        Only a small percentage of installations are selected, so an audit is
                        not a gate for every job. It is tracked here when this job is selected.
                    </Callout>
                ) : null}

                {context.requires_notice_of_completion ? (
                    <Callout tone="info" icon={Info}>
                        A Notice of Completion is the completion evidence used for the STC claim
                        in {context.state_label}.
                    </Callout>
                ) : null}
            </div>
        </SectionCard>
    );
}

function GridPanel({ job }: { job: JobDetail }) {
    const grid = job.grid;

    if (!grid) {
        return null;
    }

    const approvalExpired =
        grid.approval_expires_on !== null && new Date(grid.approval_expires_on) < new Date();

    return (
        <SectionCard
            id="grid"
            title="Grid connection"
            description="Network pre-approval and the final connection closeout are separate gates."
            icon={PlugZap}
            tone={grid.status.is_approved ? 'success' : 'warning'}
            actions={<GridStatusBadge status={grid.status} variant="pill" />}
        >
            <KeyValueGrid
                columns={2}
                items={[
                    {
                        label: 'Network (DNSP)',
                        value: grid.dnsp_website ? (
                            <a
                                href={grid.dnsp_website}
                                target="_blank"
                                rel="noreferrer"
                                className="underline-offset-4 hover:underline"
                            >
                                {grid.dnsp}
                            </a>
                        ) : (
                            grid.dnsp
                        ),
                    },
                    {
                        label: 'NMI',
                        value: grid.nmi ? (
                            <span className="crm-num font-mono text-xs">{grid.nmi}</span>
                        ) : null,
                    },
                    { label: 'Application reference', value: grid.application_reference },
                    {
                        label: 'Application submitted',
                        value: formatDateTime(grid.application_submitted_at),
                    },
                    { label: 'Approved', value: formatDateTime(grid.approved_at) },
                    {
                        label: 'Approval expires',
                        value: grid.approval_expires_on ? (
                            <span className={approvalExpired ? 'text-danger' : undefined}>
                                {formatDate(grid.approval_expires_on)}
                                {approvalExpired ? ' (expired)' : ''}
                            </span>
                        ) : null,
                    },
                    {
                        label: 'Approved export limit',
                        value:
                            grid.approved_export_limit_kw === null
                                ? null
                                : formatKilowatts(grid.approved_export_limit_kw),
                    },
                    {
                        label: 'Export enabled',
                        value: grid.export_enabled ? (
                            <StatusLabel tone="success">Export enabled</StatusLabel>
                        ) : (
                            <StatusLabel tone="warning">Not enabled</StatusLabel>
                        ),
                    },
                    { label: 'Meter type', value: grid.meter_type },
                    { label: 'Meter serial', value: grid.meter_serial },
                    { label: 'Meter installed', value: formatDateTime(grid.meter_installed_at) },
                ]}
            />

            {grid.conditions_html ? (
                <div className="border-hairline mt-4 border-t pt-4">
                    <p className="crm-label">Network conditions</p>
                    <RichText html={grid.conditions_html} className="mt-1" />
                </div>
            ) : null}

            {!grid.status.is_approved ? (
                <Callout tone="warning" icon={Zap} className="mt-4">
                    The network has not approved this connection. Stock picking and installation
                    are both gated on pre-approval, in every Australian state and territory.
                </Callout>
            ) : null}
        </SectionCard>
    );
}

function InspectionPanel({
    job,
    context,
}: {
    job: JobDetail;
    context: ComplianceContext | null;
}) {
    const inspection = job.inspection;

    if (!inspection) {
        return null;
    }

    return (
        <SectionCard
            id="inspection"
            title="Electrical inspection"
            description={
                inspection.required_by_state
                    ? 'Mandatory in this state before the system can be energised.'
                    : 'Required only when the job has been flagged for an independent inspection.'
            }
            icon={ClipboardCheck}
            tone={inspection.status.is_passed ? 'success' : 'warning'}
            actions={<InspectionBadge inspection={inspection.status} variant="pill" />}
        >
            <KeyValueGrid
                columns={2}
                items={[
                    {
                        label: 'Inspection required',
                        value: inspection.required ? 'Yes' : 'No',
                    },
                    { label: 'Authority', value: inspection.authority },
                    { label: 'Booked for', value: formatDateTime(inspection.booked_for) },
                    { label: 'Passed at', value: formatDateTime(inspection.passed_at) },
                    { label: 'Inspector', value: inspection.inspector_name },
                    { label: 'Inspector licence', value: inspection.inspector_licence },
                    {
                        label:
                            inspection.certificate.type ??
                            context?.certificate_name ??
                            'Certificate',
                        value: inspection.certificate.number,
                    },
                    {
                        label: 'Certificate issued',
                        value: formatDateTime(inspection.certificate.issued_at),
                    },
                ]}
            />

            {inspection.defects_html ? (
                <Callout tone="danger" icon={AlertTriangle} title="Defects raised" className="mt-4">
                    <RichText html={inspection.defects_html} />
                </Callout>
            ) : null}

            {inspection.notes_html ? (
                <div className="border-hairline mt-4 border-t pt-4">
                    <p className="crm-label">Inspection notes</p>
                    <RichText html={inspection.notes_html} className="mt-1" />
                </div>
            ) : null}

            {inspection.status.blocks_energising ? (
                <Callout tone="danger" icon={ShieldAlert} className="mt-4">
                    The installation has not been cleared to be energised, so grid closeout and
                    job completion are both blocked.
                </Callout>
            ) : null}
        </SectionCard>
    );
}

function SolarVictoriaPanel({
    job,
    context,
}: {
    job: JobDetail;
    context: ComplianceContext | null;
}) {
    const victoria = job.solar_victoria;

    if (!victoria) {
        return null;
    }

    return (
        <SectionCard
            id="victoria"
            title="State rebate program"
            description={victoria.program ?? context?.rebate_program ?? null}
            icon={Sun}
            tone="solar"
        >
            <KeyValueGrid
                columns={2}
                items={[
                    { label: 'Application reference', value: victoria.application_ref },
                    {
                        label: 'Rebate amount',
                        value:
                            victoria.rebate_amount === null
                                ? null
                                : formatCurrency(victoria.rebate_amount),
                    },
                    {
                        label: 'Selected for safety audit',
                        value: victoria.audit_selected ? (
                            <StatusLabel tone="warning">Selected</StatusLabel>
                        ) : (
                            <StatusLabel tone="slate" quiet>
                                Not selected
                            </StatusLabel>
                        ),
                    },
                    { label: 'Audit status', value: victoria.audit_status },
                    {
                        label: 'Audit decided',
                        value: formatDateTime(victoria.audit_decided_at),
                    },
                ]}
            />

            <p className="text-muted-foreground mt-4 text-xs leading-relaxed">
                {victoria.audit_program ?? 'The state safety audit'} checks a sample of
                installations, not every job. Where a job is selected, rectification is tracked
                here.
            </p>
        </SectionCard>
    );
}

function StcPanel({ job }: { job: JobDetail }) {
    const stc = job.stc;

    if (!stc) {
        return null;
    }

    return (
        <SectionCard
            id="stc"
            title="STC claim"
            description="Small-scale Technology Certificates under the federal scheme. Certificates are created after installation, not automatically."
            icon={CheckCircle2}
            tone={stc.status.is_open ? 'warning' : 'success'}
            actions={<StcBadge status={stc.status} variant="pill" />}
        >
            <KeyValueGrid
                columns={2}
                items={[
                    { label: 'Eligible', value: stc.eligible ? 'Yes' : 'No' },
                    { label: 'Certificates', value: formatNumber(stc.quantity) },
                    { label: 'Zone rating', value: stc.zone_rating },
                    {
                        label: 'Assignment signed',
                        value: stc.assignment_signed ? (
                            <StatusLabel tone="success">Signed</StatusLabel>
                        ) : (
                            <StatusLabel tone="warning">Outstanding</StatusLabel>
                        ),
                    },
                    { label: 'Agent', value: stc.agent },
                    { label: 'Submitted', value: formatDateTime(stc.submitted_at) },
                    { label: 'Registered', value: formatDateTime(stc.registered_at) },
                    {
                        label: 'Notice of Completion',
                        value: stc.requires_notice_of_completion
                            ? 'Required in this state'
                            : 'Not required',
                    },
                ]}
            />

            {stc.requires_notice_of_completion ? (
                <Callout tone="info" icon={Info} className="mt-4">
                    This state requires a Notice of Completion as completion evidence for the
                    claim.
                </Callout>
            ) : null}
        </SectionCard>
    );
}

/* ----------------------------------------------------------------- details */

function SystemPanel({ job }: { job: JobDetail }) {
    return (
        <SectionCard
            id="system"
            title="System specification"
            description="What is being installed, and the credentials of who designed and installed it."
            icon={Zap}
            tone="solar"
        >
            <KeyValueGrid
                columns={3}
                items={[
                    { label: 'Installation type', value: job.system.installation_config.label },
                    { label: 'System size', value: formatKilowatts(job.system.system_size_kw) },
                    { label: 'Panels', value: formatNumber(job.system.panel_count) },
                    { label: 'Panel brand', value: job.system.panel_make },
                    { label: 'Panel model', value: job.system.panel_model },
                    {
                        label: 'Panel wattage',
                        value:
                            job.system.panel_wattage === null
                                ? null
                                : `${formatNumber(job.system.panel_wattage)} W`,
                    },
                    { label: 'Strings', value: formatNumber(job.system.string_count) },
                    { label: 'Inverter brand', value: job.system.inverter_make },
                    { label: 'Inverter model', value: job.system.inverter_model },
                    { label: 'Inverter serial', value: job.system.inverter_serial },
                    {
                        label: 'Inverter capacity',
                        value: formatKilowatts(job.system.inverter_capacity_kw),
                    },
                    { label: 'Battery brand', value: job.system.battery_make },
                    { label: 'Battery model', value: job.system.battery_model },
                    {
                        label: 'Battery capacity',
                        value:
                            job.system.battery_capacity_kwh === null
                                ? null
                                : `${formatNumber(job.system.battery_capacity_kwh)} kWh`,
                    },
                    { label: 'Export limit', value: formatKilowatts(job.system.export_limit_kw) },
                    { label: 'SAA installer no.', value: job.credentials.saa_installer_number },
                    { label: 'SAA designer no.', value: job.credentials.saa_designer_number },
                    {
                        label: 'Electrical licence',
                        value: job.credentials.electrical_licence_number,
                    },
                ]}
            />

            {job.equipment.lines.length > 0 ? (
                <div className="border-hairline mt-4 border-t pt-4">
                    <p className="crm-label">Warehouse</p>
                    <ul className="mt-2 grid gap-2 sm:grid-cols-2">
                        {job.equipment.lines.map((line) => (
                            <li
                                key={line.kind}
                                className="border-hairline rounded-lg border px-3 py-2"
                            >
                                <div className="flex items-center justify-between gap-2">
                                    <span className="text-muted-foreground text-[11px] font-medium tracking-wide uppercase">
                                        {line.label}
                                    </span>
                                    {line.product ? (
                                        <span
                                            className={cn(
                                                'rounded px-1.5 py-0.5 text-[10px] font-medium',
                                                line.shortfall !== null
                                                    ? 'bg-amber-100 text-amber-900 dark:bg-amber-500/20 dark:text-amber-200'
                                                    : 'bg-emerald-100 text-emerald-900 dark:bg-emerald-500/15 dark:text-emerald-200',
                                            )}
                                        >
                                            {line.shortfall !== null
                                                ? `Short ${formatNumber(line.shortfall)}`
                                                : `${formatNumber(line.product.available)} available`}
                                        </span>
                                    ) : null}
                                </div>
                                {line.product ? (
                                    <>
                                        <div className="mt-1 text-sm">
                                            {line.product.name}
                                        </div>
                                        <div className="text-muted-foreground mt-0.5 font-mono text-[11px]">
                                            {line.product.sku}
                                            {line.needed !== null
                                                ? ` · needs ${formatNumber(line.needed)} ${line.unit}${line.needed === 1 ? '' : 's'}`
                                                : ''}
                                        </div>
                                    </>
                                ) : (
                                    <div className="text-muted-foreground mt-1 text-xs">
                                        {line.name ?? 'Unnamed'} — not in the
                                        catalogue, so its stock is not tracked.
                                    </div>
                                )}
                            </li>
                        ))}
                    </ul>
                </div>
            ) : null}

            {job.description_html ? (
                <div className="border-hairline mt-4 border-t pt-4">
                    <p className="crm-label">Scope</p>
                    <RichText html={job.description_html} className="mt-1" />
                </div>
            ) : null}
        </SectionCard>
    );
}

/* -------------------------------------------------------- stock readiness */

/**
 * Whether the warehouse can pick this job, line by line.
 *
 * This is the same subtraction the "Needs stock" queue runs, so a job that
 * appears as short in the list shows exactly which line is short here. The
 * install date is the deadline shown when there is one; how long an order
 * takes to arrive is the buyer's call, not something this screen invents.
 */
function ReadinessPanel({ job }: { job: JobDetail }) {
    const { readiness } = job;

    if (readiness.lines.length === 0) {
        return null;
    }

    const short = readiness.state === 'short';

    return (
        <SectionCard
            id="readiness"
            title="Stock readiness"
            description="What the warehouse holds against what this job needs, from the catalogue lines linked to its equipment."
            icon={short ? AlertTriangle : CheckCircle2}
            tone={short ? 'warning' : 'success'}
        >
            <p
                className={cn(
                    'rounded-lg px-3 py-2 text-sm',
                    short
                        ? 'bg-amber-100 text-amber-900 dark:bg-amber-500/20 dark:text-amber-200'
                        : 'bg-emerald-100 text-emerald-900 dark:bg-emerald-500/15 dark:text-emerald-200',
                )}
            >
                {readiness.summary}
            </p>

            <div className="border-hairline mt-3 overflow-x-auto rounded-lg border">
                <table className="w-full text-sm">
                    <thead className="bg-muted/40 text-muted-foreground text-xs">
                        <tr>
                            <th className="px-3 py-2 text-left font-medium">
                                Line
                            </th>
                            <th className="px-3 py-2 text-left font-medium">
                                Needs
                            </th>
                            <th className="px-3 py-2 text-left font-medium">
                                Available
                            </th>
                            <th className="px-3 py-2 text-left font-medium">
                                Short
                            </th>
                        </tr>
                    </thead>
                    <tbody>
                        {readiness.lines.map((line) => (
                            <tr
                                key={line.sku}
                                className="border-t border-hairline"
                            >
                                <td className="px-3 py-2">
                                    <div className="font-medium">
                                        {line.label}
                                    </div>
                                    <div className="text-muted-foreground font-mono text-[11px]">
                                        {line.sku}
                                    </div>
                                </td>
                                <td className="px-3 py-2">
                                    {formatNumber(line.needed)} {line.unit}
                                    {line.needed === 1 ? '' : 's'}
                                </td>
                                <td className="px-3 py-2">
                                    {formatNumber(line.available)}
                                </td>
                                <td className="px-3 py-2">
                                    {line.shortfall > 0 ? (
                                        <span className="font-medium text-amber-700 dark:text-amber-300">
                                            {formatNumber(line.shortfall)}
                                        </span>
                                    ) : (
                                        <span className="text-muted-foreground">
                                            —
                                        </span>
                                    )}
                                </td>
                            </tr>
                        ))}
                    </tbody>
                </table>
            </div>
        </SectionCard>
    );
}

/* ------------------------------------------------------------- parts list */

/**
 * The draft parts list saved with the job.
 *
 * Read-only here: the list is edited on the job form, where it can be drafted
 * again from the design. It is shown with its basis so the number beside a part
 * always has its arithmetic next to it, and a part the design could not
 * calculate is marked rather than left looking like a zero.
 */
function PartsPanel({ job }: { job: JobDetail }) {
    if (job.parts.length === 0) {
        return null;
    }

    return (
        <SectionCard
            id="parts"
            title="Installation parts"
            description="The draft list saved with this job, as it was prepared on the job form."
            icon={Package}
            tone="slate"
        >
            <div className="overflow-x-auto rounded-lg border border-hairline">
                <table className="w-full text-sm">
                    <thead className="bg-muted/40 text-muted-foreground text-xs">
                        <tr>
                            <th className="px-3 py-2 text-left font-medium">
                                Part
                            </th>
                            <th className="w-32 px-3 py-2 text-left font-medium">
                                Quantity
                            </th>
                            <th className="px-3 py-2 text-left font-medium">
                                Basis
                            </th>
                        </tr>
                    </thead>
                    <tbody>
                        {job.parts.map((part) => (
                            <tr
                                key={part.id}
                                className="border-t border-hairline align-top"
                            >
                                <td className="px-3 py-2">
                                    {part.description}
                                </td>
                                <td className="px-3 py-2">
                                    {part.quantity === null ? (
                                        <span className="inline-flex rounded bg-amber-100 px-1.5 py-0.5 text-[10px] font-medium text-amber-900 dark:bg-amber-500/20 dark:text-amber-200">
                                            Needs review
                                        </span>
                                    ) : (
                                        <>
                                            {formatNumber(part.quantity)}{' '}
                                            <span className="text-muted-foreground text-xs">
                                                {part.unit}
                                            </span>
                                        </>
                                    )}
                                </td>
                                <td className="text-muted-foreground px-3 py-2 text-xs">
                                    {part.basis ?? '—'}
                                </td>
                            </tr>
                        ))}
                    </tbody>
                </table>
            </div>

            <p className="text-muted-foreground mt-3 text-xs">
                Draft only. Check every quantity and rating against the
                equipment manufacturer&rsquo;s instructions, the site design and
                AS/NZS 5033, 4777.1 and 5139. This list is not a compliance
                record.
            </p>
        </SectionCard>
    );
}

function DatesPanel({ job }: { job: JobDetail }) {
    return (
        <SectionCard
            id="dates"
            title="Milestone dates"
            description="Every date the reporting relies on."
            icon={CalendarClock}
        >
            <KeyValueGrid
                columns={3}
                items={milestoneFields.map((field) => ({
                    label: field.label,
                    value: field.dateOnly
                        ? formatDate(job.dates[field.key] ?? null)
                        : formatDateTime(job.dates[field.key] ?? null),
                }))}
            />
        </SectionCard>
    );
}

function AssignmentsPanel({ job }: { job: JobDetail }) {
    return (
        <SectionCard
            id="assignments"
            title="Assignments"
            description="Who owns this job, at each step of the process."
            icon={Users}
        >
            <KeyValueGrid
                columns={2}
                items={[
                    { label: 'Account owner', value: job.owner?.name },
                    { label: 'Sales rep', value: job.sales_rep?.name },
                    { label: 'Operations manager', value: job.operations_manager?.name },
                    { label: 'Installer', value: job.installer?.name },
                ]}
            />
        </SectionCard>
    );
}

/* ----------------------------------------------------------------- owners */

/**
 * Whose house the system is on.
 *
 * A property is sold with the job still open often enough that this is not an
 * edge case: the work belongs to whoever held the house when it was booked, and
 * the work still to do belongs to whoever holds it now. Both are on the job, and
 * the handover that changed it is recorded underneath.
 */
function OwnersPanel({
    job,
    can,
    options,
}: {
    job: JobDetail;
    can: { transferOwnership: boolean };
    options: OptionsBundle;
}) {
    const ownership = job.owners;
    const heldOn = ownership?.held_on ?? null;
    const current = ownership?.current ?? null;

    return (
        <SectionCard
            id="owners"
            title="Owners of this property"
            description="Who held the house when this job was created, and who holds it now. A property outlives its owners, and so does the work on it."
            icon={House}
            tone="violet"
            actions={
                can.transferOwnership && job.site ? (
                    <TransferOwnershipDialog
                        ownerId={job.site.owner_id}
                        site={{
                            id: job.site.id,
                            label: job.site.label,
                            address: job.site.address,
                        }}
                        currentOwnerName={job.customer?.name ?? null}
                        reasons={options.ownershipReasons}
                        customerTypes={options.customerTypes}
                    />
                ) : null
            }
        >
            {ownership === null || ownership.history.length === 0 ? (
                <EmptyState
                    icon={House}
                    title="No owner is recorded"
                    description="This property has no ownership history yet, so there is nothing to say about who held it when this job was created."
                />
            ) : (
                <div className="grid gap-4">
                    {heldOn !== null && !heldOn.is_current && current !== null ? (
                        <Callout tone="info" icon={History}>
                            This job was created while the property belonged to{' '}
                            <span className="text-foreground font-medium">
                                {heldOn.owner.name}
                            </span>
                            . It has since been handed to{' '}
                            <span className="text-foreground font-medium">
                                {current.owner.name}
                            </span>
                            , which is who the address is dealt with under now.
                        </Callout>
                    ) : null}

                    <OwnershipHistory
                        ownership={ownership}
                        markedId={heldOn?.id ?? null}
                        defaultOpen
                    />
                </div>
            )}
        </SectionCard>
    );
}

function FinancialsPanel({ job }: { job: JobDetail }) {
    const financials = job.financials;

    if (!financials) {
        return null;
    }

    return (
        <SectionCard
            id="financials"
            title="Financials"
            description="Contract value, deposit and any STC discount applied."
            icon={DollarSign}
            tone="info"
        >
            <KeyValueGrid
                columns={2}
                items={[
                    {
                        label: 'Contract value',
                        value: formatCurrency(financials.contract_value),
                    },
                    { label: 'Deposit', value: formatCurrency(financials.deposit_amount) },
                    {
                        label: 'Deposit received',
                        value: formatDateTime(financials.deposit_received_at),
                    },
                    {
                        label: 'STC discount',
                        value: formatCurrency(financials.stc_discount_amount),
                    },
                    { label: 'Finance provider', value: financials.finance_provider },
                    {
                        label: 'Finance term',
                        value:
                            financials.finance_term_months === null
                                ? null
                                : `${financials.finance_term_months} months`,
                    },
                ]}
            />
        </SectionCard>
    );
}

/* --------------------------------------------------------------- documents */

function DocumentsPanel({
    documents,
    outstanding,
    canUpload,
}: {
    documents: DocumentSummary[];
    outstanding: Option[];
    canUpload: boolean;
}) {
    return (
        <SectionCard
            id="documents"
            title="Documents and evidence"
            description="The job pack, certificates and the STC supporting evidence."
            icon={FileUp}
            tone={outstanding.length > 0 ? 'warning' : 'success'}
            actions={
                <Button
                    variant="outline"
                    size="sm"
                    disabled
                    title="Document upload is delivered with the documents module."
                >
                    <Plus />
                    Upload document
                </Button>
            }
        >
            {outstanding.length > 0 ? (
                <div className="border-hairline mb-4 rounded-lg border border-dashed p-3">
                    <p className="crm-label">Still outstanding</p>
                    <div className="mt-2 flex flex-wrap gap-1.5">
                        {outstanding.map((item) => (
                            <RuleFlag key={item.value} tone="warning">
                                {item.label}
                            </RuleFlag>
                        ))}
                    </div>
                </div>
            ) : (
                <Callout tone="success" icon={CheckCircle2} className="mb-4">
                    All required evidence has been received.
                </Callout>
            )}

            {documents.length === 0 ? (
                <EmptyState
                    icon={FileUp}
                    title="No documents uploaded yet"
                    description={
                        canUpload
                            ? 'Quotes, design drawings, certificates and inspection reports will appear here.'
                            : 'You do not have permission to upload documents for this job.'
                    }
                />
            ) : (
                <Table>
                    <TableHeader>
                        <TableRow>
                            <TableHead>Document</TableHead>
                            <TableHead>Category</TableHead>
                            <TableHead>Uploaded by</TableHead>
                            <TableHead>Added</TableHead>
                            <TableHead>Status</TableHead>
                        </TableRow>
                    </TableHeader>
                    <TableBody>
                        {documents.map((document) => (
                            <TableRow key={document.id}>
                                <TableCell>
                                    <span className="font-medium">{document.title}</span>
                                    <span className="text-muted-foreground block text-xs">
                                        {document.original_name} · {document.human_size}
                                    </span>
                                </TableCell>
                                <TableCell>
                                    <span className="flex flex-wrap items-center gap-1.5">
                                        <StatusPill tone="slate">
                                            {document.category.label}
                                        </StatusPill>
                                        {document.category.is_stc_evidence ? (
                                            <StatusPill tone="success">STC evidence</StatusPill>
                                        ) : null}
                                    </span>
                                </TableCell>
                                <TableCell className="text-sm">
                                    {document.uploaded_by?.name ?? '—'}
                                </TableCell>
                                <TableCell className="text-muted-foreground text-sm">
                                    {formatDate(document.created_at)}
                                </TableCell>
                                <TableCell>
                                    {document.is_verified ? (
                                        <StatusLabel tone="success">Verified</StatusLabel>
                                    ) : (
                                        <StatusLabel tone="slate" quiet>
                                            Unverified
                                        </StatusLabel>
                                    )}
                                </TableCell>
                            </TableRow>
                        ))}
                    </TableBody>
                </Table>
            )}
        </SectionCard>
    );
}

/* ---------------------------------------------------------------- activity */

function ActivityPanel({
    job,
    activityTypes,
    activities,
}: {
    job: JobDetail;
    activityTypes: Option[];
    activities: ActivityEntry[];
}) {
    return (
        <SectionCard
            id="activity"
            title="Activity timeline"
            description="Calls, emails, site visits and notes recorded against this job."
            icon={History}
        >
            <Form
                {...JobActivityController.store.form({ job: job.id })}
                options={{ preserveScroll: true }}
                className="bg-surface/50 ring-hairline space-y-3 rounded-lg p-3 ring-1 ring-inset"
            >
                {({ processing, errors }) => (
                    <>
                        <div className="grid gap-3 sm:grid-cols-2">
                            <FormField
                                label="Type"
                                htmlFor="activity-type"
                                error={errors.type}
                            >
                                <NativeSelect
                                    id="activity-type"
                                    name="type"
                                    defaultValue="note"
                                    required
                                >
                                    {activityTypes.map((type) => (
                                        <option key={type.value} value={type.value}>
                                            {type.label}
                                        </option>
                                    ))}
                                </NativeSelect>
                            </FormField>

                            <FormField
                                label="Subject"
                                htmlFor="activity-subject"
                                error={errors.subject}
                            >
                                <Input
                                    id="activity-subject"
                                    name="subject"
                                    placeholder="Spoke with the customer"
                                />
                            </FormField>
                        </div>

                        <FormField label="Details" htmlFor="activity-body" error={errors.body}>
                            <textarea
                                id="activity-body"
                                name="body"
                                rows={3}
                                className={textareaClassName}
                                placeholder="What happened, and what is the next action?"
                            />
                        </FormField>

                        <Button
                            type="submit"
                            variant="outline"
                            size="sm"
                            disabled={processing}
                        >
                            <Plus />
                            Log activity
                        </Button>
                    </>
                )}
            </Form>

            <div className="border-hairline mt-5 border-t pt-5">
                {activities.length === 0 ? (
                    <EmptyState icon={History} title="No activity recorded yet" />
                ) : (
                    <ActivityTimeline entries={activities} />
                )}
            </div>
        </SectionCard>
    );
}

/* ----------------------------------------------------------- status history */

function StatusHistoryPanel({ history }: { history: StatusHistoryEntry[] }) {
    return (
        <SectionCard
            id="history"
            title="Status audit trail"
            description="An append-only record of every pipeline change, who made it and why. This is the compliance record."
            icon={Clock}
        >
            {history.length === 0 ? (
                <EmptyState icon={Clock} title="No status changes recorded yet" />
            ) : (
                <StatusAuditTrail entries={history} />
            )}
        </SectionCard>
    );
}


/**
 * The inspection and warranty reports raised against this job.
 *
 * A handover, a warranty claim and a defect recheck are the same report filled
 * in at different times, so they sit together rather than in three places. The
 * crew answers each through its own link, with no account.
 */
function InspectionsPanel({
    inspections,
    canRequest,
    jobId,
}: {
    inspections: InspectionSummary[];
    canRequest: boolean;
    jobId: number;
}) {
    const open = inspections.filter(
        (report) => report.status.value === 'invited',
    ).length;
    const unread = inspections.filter(
        (report) => report.status.value === 'submitted',
    ).length;

    return (
        <SectionCard
            id="inspections"
            title="Inspections & warranty"
            description="Standard Australian inspection reports for this install: handover, a warranty claim, or a defect recheck."
            icon={ClipboardCheck}
            tone={unread > 0 ? 'warning' : 'emerald'}
            flush
            bodyClassName="divide-hairline divide-y"
            actions={
                <>
                    {open > 0 ? (
                        <StatusPill tone="amber">{open} with a crew</StatusPill>
                    ) : null}
                    {unread > 0 ? (
                        <StatusPill tone="blue">{unread} to read</StatusPill>
                    ) : null}
                    {canRequest ? (
                        <Button variant="outline" size="sm" asChild>
                            <Link href={`/inspections/create?job=${jobId}`}>
                                <Plus className="size-4" />
                                Request inspection
                            </Link>
                        </Button>
                    ) : null}
                </>
            }
        >
            {inspections.length === 0 ? (
                <EmptyState
                    icon={ClipboardCheck}
                    title="No inspection reports"
                    description="Request one and share the link with the crew. They fill in the checklist, test results, photos and signature on site, and it comes back here."
                    className="py-8"
                    action={
                        canRequest ? (
                            <Button asChild variant="outline" size="sm">
                                <Link href={`/inspections/create?job=${jobId}`}>
                                    Request inspection
                                </Link>
                            </Button>
                        ) : null
                    }
                />
            ) : (
                inspections.map((report) => (
                    <Link
                        key={report.id}
                        href={`/inspections/${report.id}`}
                        className="hover:bg-muted/40 flex flex-wrap items-center gap-x-4 gap-y-1 px-5 py-3 transition-colors"
                    >
                        <span className="crm-num w-24 shrink-0 text-sm font-medium">
                            {report.reference}
                        </span>
                        <span className="min-w-0 flex-1">
                            <span className="block truncate text-sm">
                                {report.purpose.label}
                            </span>
                            <span className="text-muted-foreground block text-xs">
                                {report.inspector_name ??
                                    report.installer?.name ??
                                    'Not sent to a crew yet'}
                                {report.inspected_on
                                    ? ` · inspected ${formatDate(report.inspected_on)}`
                                    : ''}
                                {report.defect_count > 0
                                    ? ` · ${report.defect_count} defect${
                                          report.defect_count === 1 ? '' : 's'
                                      }`
                                    : ''}
                            </span>
                        </span>
                        {report.outcome ? (
                            <StatusPill tone={report.outcome.color}>
                                {report.outcome.label}
                            </StatusPill>
                        ) : null}
                        <StatusPill tone={report.status.color}>
                            {report.status.label}
                        </StatusPill>
                    </Link>
                ))
            )}
        </SectionCard>
    );
}

/**
 * Support tickets raised against this job. A complaint that arrives by SMS is
 * attached to the job it is about, with the installer who did the work already
 * on it, so the crew that installed the system sees what the customer reported.
 */
function TicketsPanel({
    tickets,
    counts,
    canCreate,
    jobId,
}: {
    tickets: TicketSummary[];
    counts: TicketCounts;
    canCreate: boolean;
    jobId: number;
}) {
    const hasOpenTickets = counts.open > 0;

    return (
        <SectionCard
            id="tickets"
            title="Support tickets"
            description="Complaints and service requests raised against this job, oldest open ticket first."
            icon={LifeBuoy}
            tone={hasOpenTickets ? 'warning' : 'slate'}
            flush
            bodyClassName="divide-hairline divide-y"
            actions={
                <>
                    <StatusPill tone={hasOpenTickets ? 'warning' : 'slate'}>
                        {counts.open} open of {counts.total}
                    </StatusPill>
                    {canCreate ? (
                        <Button variant="outline" size="sm" asChild>
                            <Link
                                href={ticketsCreate({
                                    query: { job: jobId },
                                })}
                            >
                                <Plus className="size-4" />
                                Log ticket
                            </Link>
                        </Button>
                    ) : null}
                </>
            }
        >
            {tickets.length === 0 ? (
                <EmptyState
                    icon={LifeBuoy}
                    title="No support tickets"
                    description="Nothing has been reported against this job. Complaints that arrive from the SMS system land here with the installer already attached."
                    className="py-8"
                    action={
                        canCreate ? (
                            <Button asChild variant="outline" size="sm">
                                <Link
                                    href={ticketsCreate({
                                        query: { job: jobId },
                                    })}
                                >
                                    Log ticket
                                </Link>
                            </Button>
                        ) : null
                    }
                />
            ) : (
                tickets.map((ticket) => (
                    <TicketRow key={ticket.id} ticket={ticket} />
                ))
            )}
        </SectionCard>
    );
}

/**
 * One ticket in a flushed card: what was reported, where it is up to, who owns
 * it and how long it has been open.
 */
function TicketRow({ ticket }: { ticket: TicketSummary }) {
    const isClosed = ticket.status.is_terminal;

    return (
        <Link
            href={ticketShow(ticket.id).url}
            className="hover:bg-accent/40 flex flex-col gap-2 px-5 py-3.5 transition-colors lg:flex-row lg:items-center lg:gap-4"
        >
            <div className="flex min-w-0 flex-1 items-start gap-3">
                <StatusDot
                    tone={ticket.status.color}
                    className={cn('mt-1.5', isClosed && 'opacity-50')}
                />

                <div className="min-w-0">
                    <div className="flex flex-wrap items-baseline gap-x-2 gap-y-0.5">
                        <span className="font-mono text-xs font-medium">
                            {ticket.display_number}
                        </span>
                        <span
                            className={cn(
                                'truncate text-sm font-medium',
                                isClosed && 'text-muted-foreground',
                            )}
                        >
                            {ticket.issue}
                        </span>
                    </div>

                    <div className="text-muted-foreground mt-0.5 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs">
                        <StatusLabel
                            tone={ticket.status.color}
                            quiet={isClosed}
                            className="text-xs"
                        >
                            {ticket.status.label}
                        </StatusLabel>
                        <span aria-hidden className="text-muted-foreground/40">
                            ·
                        </span>
                        <span>{ticket.installer?.name ?? 'Unassigned'}</span>
                        <span aria-hidden className="text-muted-foreground/40">
                            ·
                        </span>
                        <span>{ticket.source.label}</span>
                        {ticket.ticket_import_id ? (
                            <span className="text-muted-foreground/70 font-mono text-[11px]">
                                {ticket.ticket_import_id}
                            </span>
                        ) : null}
                    </div>
                </div>
            </div>

            <div className="flex shrink-0 flex-wrap items-center gap-2 lg:justify-end">
                <PriorityBadge priority={ticket.priority} />
                <span
                    className={cn(
                        'crm-num text-xs font-medium',
                        ticket.is_overdue
                            ? 'text-danger'
                            : 'text-muted-foreground',
                    )}
                >
                    {formatDuration(ticket.days_open)}
                </span>
                {ticket.is_overdue ? (
                    <StatusPill tone="danger">Overdue</StatusPill>
                ) : null}
            </div>
        </Link>
    );
}

JobShow.layout = {
    breadcrumbs: [
        { title: 'Jobs', href: jobsIndex() },
        { title: 'Job detail', href: '#' },
    ],
};
