import { z } from 'zod';
import { BERTH_STATUSES } from '@/lib/constants';
import { baseListQuerySchema } from '@/lib/api/list-query';

// ─── Create Berth ────────────────────────────────────────────────────────────

export const createBerthSchema = z.object({
  mooringNumber: z.string().min(1),
  area: z.string().min(1),
  lengthFt: z.coerce.number().optional(),
  lengthM: z.coerce.number().optional(),
  widthFt: z.coerce.number().optional(),
  widthM: z.coerce.number().optional(),
  draftFt: z.coerce.number().optional(),
  draftM: z.coerce.number().optional(),
  price: z.coerce.number().optional(),
  priceCurrency: z.string().optional(),
  status: z.enum(BERTH_STATUSES).default('available'),
  tenureType: z.enum(['permanent', 'fixed_term']).optional(),
  mooringType: z.string().optional(),
  powerCapacity: z.coerce.number().optional(), // kW
  voltage: z.coerce.number().optional(), // V at 60Hz
  access: z.string().optional(),
  bowFacing: z.string().optional(),
  sidePontoon: z.string().optional(),
});

export type CreateBerthInput = z.infer<typeof createBerthSchema>;

// ─── Bulk Add Berths ─────────────────────────────────────────────────────────

export const bulkAddBerthsSchema = z.object({
  /** Per-row create payloads. Each row must carry a unique mooringNumber;
   *  the service rejects with ConflictError on first duplicate (either
   *  within the input array OR against an existing port row). */
  berths: z.array(createBerthSchema).min(1).max(500),
});

export type BulkAddBerthsInput = z.infer<typeof bulkAddBerthsSchema>;

// ─── Update Berth ─────────────────────────────────────────────────────────────

export const updateBerthSchema = z.object({
  area: z.string().optional(),
  lengthFt: z.coerce.number().optional(),
  lengthM: z.coerce.number().optional(),
  widthFt: z.coerce.number().optional(),
  widthM: z.coerce.number().optional(),
  draftFt: z.coerce.number().optional(),
  draftM: z.coerce.number().optional(),
  widthIsMinimum: z.boolean().optional(),
  nominalBoatSize: z.coerce.number().optional(), // ft
  nominalBoatSizeM: z.coerce.number().optional(), // m
  waterDepth: z.coerce.number().optional(),
  waterDepthM: z.coerce.number().optional(),
  waterDepthIsMinimum: z.boolean().optional(),
  sidePontoon: z.string().optional(),
  powerCapacity: z.coerce.number().optional(), // kW
  voltage: z.coerce.number().optional(), // V at 60Hz
  mooringType: z.string().optional(),
  cleatType: z.string().optional(),
  cleatCapacity: z.string().optional(),
  bollardType: z.string().optional(),
  bollardCapacity: z.string().optional(),
  access: z.string().optional(),
  price: z.coerce.number().optional(),
  priceCurrency: z.string().optional(),
  bowFacing: z.string().optional(),
  berthApproved: z.boolean().optional(),
  tenureType: z.enum(['permanent', 'fixed_term']).optional(),
  tenureYears: z.coerce.number().int().optional(),
  tenureStartDate: z.string().optional(),
  tenureEndDate: z.string().optional(),
});

export type UpdateBerthInput = z.infer<typeof updateBerthSchema>;

// ─── Update Berth Status ──────────────────────────────────────────────────────

export const updateBerthStatusSchema = z.object({
  status: z.enum(BERTH_STATUSES),
  reason: z.string().min(1, 'Reason is required'),
  /**
   * Optional: when status moves to under_offer or sold, the rep can pin
   * the interest that triggered the change. We auto-create a primary
   * interest_berths link for the chosen interest so the timeline +
   * heat scorer attribute the deal correctly.
   */
  interestId: z.string().min(1).optional(),
});

export type UpdateBerthStatusInput = z.infer<typeof updateBerthStatusSchema>;

// ─── Reset Manual Override ──────────────────────────────────────────────────
// Drops the manual pin so the berth resumes derived/automatic status. A reason
// is required so the audit trail records why the rep stopped pinning.
export const resetBerthOverrideSchema = z.object({
  reason: z.string().trim().min(1, 'Reason is required'),
});

export type ResetBerthOverrideInput = z.infer<typeof resetBerthOverrideSchema>;

// ─── Archive Berth ────────────────────────────────────────────────────────────

// Post-audit F5: archive replaces hard-delete. A `reason` is required so
// the audit trail captures intent - "decommissioned 2026", "duplicate of
// A3", etc. min(5) blocks one-letter throwaways.
export const archiveBerthSchema = z.object({
  reason: z.string().trim().min(5, 'Reason must be at least 5 characters'),
});

export type ArchiveBerthInput = z.infer<typeof archiveBerthSchema>;

// ─── List Berths ──────────────────────────────────────────────────────────────

export const listBerthsSchema = baseListQuerySchema.extend({
  status: z.enum(BERTH_STATUSES).optional(),
  area: z.string().optional(),
  minLength: z.coerce.number().optional(),
  maxLength: z.coerce.number().optional(),
  minPrice: z.coerce.number().optional(),
  maxPrice: z.coerce.number().optional(),
  tenureType: z.enum(['permanent', 'fixed_term']).optional(),
  tagIds: z
    .string()
    .optional()
    .transform((v) => (v ? v.split(',') : undefined)),
});

export type ListBerthsQuery = z.infer<typeof listBerthsSchema>;

// ─── Update Berth Price ───────────────────────────────────────────────────────
// Single + bulk price update. Carved out from the general updateBerthSchema
// so that callers with the `berths.update_prices` permission (but NOT the
// generic `berths.edit`) can only mutate price + currency, not dimensions
// or mooring metadata.

export const updateBerthPriceSchema = z.object({
  price: z.coerce.number().min(0, 'Price must be ≥ 0').nullable(),
  priceCurrency: z
    .string()
    .trim()
    .length(3, 'Currency must be a 3-letter ISO code')
    .toUpperCase()
    .optional(),
});

export type UpdateBerthPriceInput = z.infer<typeof updateBerthPriceSchema>;

export const bulkUpdateBerthPricesSchema = z.object({
  updates: z
    .array(
      z.object({
        berthId: z.string().uuid(),
        price: z.coerce.number().min(0).nullable(),
        priceCurrency: z.string().trim().length(3).toUpperCase().optional(),
      }),
    )
    .min(1, 'At least one berth required')
    .max(500, 'Bulk price updates capped at 500 berths per call'),
});

export type BulkUpdateBerthPricesInput = z.infer<typeof bulkUpdateBerthPricesSchema>;

// ─── Add Maintenance Log ──────────────────────────────────────────────────────

export const addMaintenanceLogSchema = z.object({
  category: z.enum(['routine', 'repair', 'inspection', 'upgrade']),
  description: z.string().min(1),
  // `null` accepted (and treated as "no value") so the shared add/edit dialog
  // can send a single body shape — an empty optional field maps to null.
  cost: z.coerce.number().nullable().optional(),
  costCurrency: z.string().optional(),
  responsibleParty: z.string().nullable().optional(),
  performedDate: z.string().min(1, 'Performed date is required'),
  photoFileIds: z.array(z.string()).optional(),
});

export type AddMaintenanceLogInput = z.infer<typeof addMaintenanceLogSchema>;

// ─── Update Maintenance Log ───────────────────────────────────────────────────

// Partial of the add schema — every field optional so the edit dialog can
// PATCH just the touched columns. `cost` and `responsibleParty` accept `null`
// so the rep can clear a previously-recorded value (an empty edit field maps
// to null in the UI, not to 0 / '').
export const updateMaintenanceLogSchema = z.object({
  category: z.enum(['routine', 'repair', 'inspection', 'upgrade']).optional(),
  description: z.string().min(1).optional(),
  cost: z.coerce.number().nullable().optional(),
  costCurrency: z.string().optional(),
  responsibleParty: z.string().nullable().optional(),
  performedDate: z.string().min(1).optional(),
  photoFileIds: z.array(z.string()).optional(),
});

export type UpdateMaintenanceLogInput = z.infer<typeof updateMaintenanceLogSchema>;

// ─── Update Waiting List ──────────────────────────────────────────────────────

export const updateWaitingListSchema = z.object({
  entries: z.array(
    z.object({
      clientId: z.string(),
      position: z.number().int().min(1),
      priority: z.enum(['normal', 'high']).optional(),
      notifyPref: z.enum(['email', 'in_app', 'both']).optional(),
      notes: z.string().optional(),
    }),
  ),
});

export type UpdateWaitingListInput = z.infer<typeof updateWaitingListSchema>;
