import { z } from "zod";

export const ROLES = ["SUPER_ADMIN", "ADMIN", "MANAGER", "STAFF"] as const;
export const PERIODS = ["WEEKLY", "MONTHLY", "QUARTERLY"] as const;
export const KPI_TYPES = ["DEPARTMENT", "PERSONAL"] as const;
export const KPI_CATEGORIES = [
  "ACQUISITION",
  "CONVERSION",
  "REVENUE",
  "SPEND",
  "ENGAGEMENT",
  "BRAND",
  "OUTPUT",
  "OTHER",
] as const;
export const TASK_STATUSES = [
  "BACKLOG",
  "TO_DO",
  "IN_PROGRESS",
  "REVIEW",
  "COMPLETED",
] as const;
export const PRIORITIES = ["LOW", "MEDIUM", "HIGH"] as const;
export const POST_STATUSES = [
  "DRAFT",
  "PENDING_APPROVAL",
  "APPROVED",
  "REJECTED",
  "POSTED",
] as const;
export const CHANNELS = [
  "META",
  "GOOGLE",
  "TIKTOK",
  "OFFLINE",
  "SEO",
  "EMAIL",
  "OTHER",
] as const;

// ─── Users ─────────────────────────────────────────────────────────────────────
export const createUserSchema = z
  .object({
    name: z.string().min(1, "Name is required"),
    email: z.string().email(),
    password: z.string().min(6, "Min 6 characters"),
    role: z.enum(ROLES),
    position: z.string().trim().optional().nullable(),
    departmentId: z.string().uuid().optional().nullable(),
  })
  .refine((d) => d.role !== "STAFF" || !!d.position, {
    message: "Staff users must have a position",
    path: ["position"],
  });

export const updateUserSchema = z.object({
  name: z.string().min(1).optional(),
  email: z.string().email().optional(),
  password: z.string().min(6).optional(),
  role: z.enum(ROLES).optional(),
  position: z.string().trim().optional().nullable(),
  departmentId: z.string().uuid().optional().nullable(),
  active: z.boolean().optional(),
});

// ─── KPI Indicators ─────────────────────────────────────────────────────────────
// Base object WITHOUT refinements — Zod 4 forbids `.partial()` on a refined
// schema, so we derive the create (refined) and update (partial) schemas from
// this plain object.
// NOTE: no `.default()` on optional fields here — Zod keeps defaults active
// through `.partial()`, which would clobber existing values on a partial PATCH
// (e.g. omitting `weightage` would reset it to 0). Defaults are applied in the
// create handler instead.
const kpiBaseSchema = z.object({
  name: z.string().min(1, "Name is required"),
  description: z.string().trim().max(2000).optional().nullable(),
  targetValue: z.coerce.number(),
  currentValue: z.coerce.number().optional(),
  unit: z.string().min(1, "Unit is required"),
  period: z.enum(PERIODS),
  type: z.enum(KPI_TYPES),
  category: z.enum(KPI_CATEGORIES).optional(),
  // DEPARTMENT KPIs roll up to a company.
  companyId: z.string().uuid().optional().nullable(),
  // The functional department that owns this KPI.
  departmentId: z.string().uuid().optional().nullable(),
  // PERSONAL KPIs are assigned to one specific staff member.
  assignedUserId: z.string().uuid().optional().nullable(),
  assignedPosition: z.string().trim().optional().nullable(),
  // A KPI can span multiple marketing channels (editable list).
  channelIds: z.array(z.string().uuid()).optional(),
  weightage: z.coerce.number().min(0).max(100).optional(),
});

export const kpiSchema = kpiBaseSchema.refine(
  (d) => d.type !== "PERSONAL" || !!d.assignedUserId,
  { message: "Personal KPIs must be assigned to a staff member", path: ["assignedUserId"] }
);

export const kpiUpdateSchema = kpiBaseSchema.partial();

// Upsert a KPI's value for one calendar month.
export const kpiMonthlyValueSchema = z.object({
  year: z.coerce.number().int().min(2000).max(2100),
  month: z.coerce.number().int().min(1).max(12),
  targetValue: z.coerce.number().optional().nullable(),
  actualValue: z.coerce.number().optional().default(0),
  // Remark explaining the result (used especially for flagged/off-target KPIs).
  note: z.string().trim().max(2000).optional().nullable(),
});

// ─── Appraisals (half-year staff review) ────────────────────────────────────────
export const APPRAISAL_HALVES = ["H1", "H2"] as const;
export const APPRAISAL_BANDS = ["OUTSTANDING", "EXCEEDS", "MEETS", "BELOW"] as const;
export const APPRAISAL_ACTIONS = [
  "request_self", // DRAFT → SELF_REVIEW
  "submit_self", // SELF_REVIEW → MANAGER_REVIEW
  "finalize", // MANAGER_REVIEW → FINALIZED
  "acknowledge", // FINALIZED → ACKNOWLEDGED
  "reopen", // back to DRAFT
] as const;

// Default competencies seeded onto each new appraisal.
export const DEFAULT_COMPETENCIES = [
  "Quality of Work",
  "Productivity & Output",
  "Communication",
  "Initiative & Ownership",
  "Teamwork & Collaboration",
  "Reliability & Punctuality",
] as const;

export const appraisalCreateSchema = z.object({
  userId: z.string().uuid(),
  year: z.coerce.number().int().min(2000).max(2100),
  half: z.enum(APPRAISAL_HALVES),
});

const competencyInputSchema = z.object({
  id: z.string().uuid(),
  score: z.coerce.number().int().min(1).max(5).optional().nullable(),
  note: z.string().trim().max(2000).optional().nullable(),
});

export const appraisalUpdateSchema = z.object({
  // Manager fields
  overallBand: z.enum(APPRAISAL_BANDS).optional().nullable(),
  strengths: z.string().trim().max(4000).optional().nullable(),
  improvements: z.string().trim().max(4000).optional().nullable(),
  managerComments: z.string().trim().max(4000).optional().nullable(),
  remarks: z.string().trim().max(4000).optional().nullable(),
  goalsReview: z.string().trim().max(4000).optional().nullable(),
  goalsNext: z.string().trim().max(4000).optional().nullable(),
  // Staff self-assessment
  selfComments: z.string().trim().max(4000).optional().nullable(),
  selfRating: z.coerce.number().int().min(1).max(5).optional().nullable(),
  // Competency scores
  competencies: z.array(competencyInputSchema).optional(),
  // Workflow transition
  action: z.enum(APPRAISAL_ACTIONS).optional(),
});

// ─── Companies ───────────────────────────────────────────────────────────────────
export const companySchema = z.object({
  name: z.string().min(1, "Name is required"),
  active: z.boolean().optional().default(true),
});

// ─── Departments ────────────────────────────────────────────────────────────────
// Functional teams (Marketing, Admin, Finance, Operations…) that own KPIs/staff.
export const departmentSchema = z.object({
  name: z.string().min(1, "Name is required"),
  active: z.boolean().optional().default(true),
  // Cross-cutting (e.g. HQ) — serves every department & company.
  spansAll: z.boolean().optional(),
});

// Per-company monthly financials & funnel inputs (executive dashboard engine).
export const companyFinancialsSchema = z.object({
  year: z.coerce.number().int().min(2000).max(2100),
  month: z.coerce.number().int().min(1).max(12),
  budget: z.coerce.number().min(0).optional(),
  adSpend: z.coerce.number().min(0).optional(),
  revenue: z.coerce.number().min(0).optional(),
  leads: z.coerce.number().int().min(0).optional(),
  trials: z.coerce.number().int().min(0).optional(),
  enrollments: z.coerce.number().int().min(0).optional(),
});

// ─── Marketing channels (editable list, multi-select on KPIs) ─────────────────────
export const channelSchema = z.object({
  name: z.string().min(1, "Name is required"),
  active: z.boolean().optional().default(true),
});

// ─── Platforms ───────────────────────────────────────────────────────────────────
export const platformSchema = z.object({
  name: z.string().min(1, "Name is required"),
  active: z.boolean().optional().default(true),
});

// ─── Scheduled posts ───────────────────────────────────────────────────────────────
export const createPostSchema = z.object({
  title: z.string().min(1, "Title is required"),
  content: z.string().min(1, "Content is required"),
  mediaUrl: z.string().trim().optional().nullable(),
  mediaNotes: z.string().trim().optional().nullable(),
  scheduledDate: z.string().datetime().optional().nullable(),
  // At least one platform must be selected.
  platformIds: z.array(z.string().uuid()).min(1, "Select at least one platform"),
  // Author can save a draft or submit straight for approval.
  submit: z.boolean().optional().default(false),
});

export const updatePostSchema = z.object({
  title: z.string().min(1).optional(),
  content: z.string().min(1).optional(),
  mediaUrl: z.string().trim().optional().nullable(),
  mediaNotes: z.string().trim().optional().nullable(),
  scheduledDate: z.string().datetime().optional().nullable(),
  platformIds: z.array(z.string().uuid()).min(1).optional(),
  // Workflow action — one of these drives a status transition.
  action: z.enum(["submit", "approve", "reject", "post", "reopen"]).optional(),
  reviewNote: z.string().trim().optional().nullable(),
  postedDate: z.string().datetime().optional().nullable(),
});

// ─── Tasks ───────────────────────────────────────────────────────────────────────
export const createTaskSchema = z.object({
  title: z.string().min(1, "Title is required"),
  description: z.string().optional().nullable(),
  status: z.enum(TASK_STATUSES).optional().default("TO_DO"),
  priority: z.enum(PRIORITIES).optional().default("MEDIUM"),
  channel: z.enum(CHANNELS).optional().nullable(),
  dueDate: z.string().datetime().optional().nullable(),
  // assignee — only used by manager-tier (admin override). Staff create for self.
  userId: z.string().uuid().optional(),
});

export const updateTaskSchema = z.object({
  title: z.string().min(1).optional(),
  description: z.string().optional().nullable(),
  status: z.enum(TASK_STATUSES).optional(),
  priority: z.enum(PRIORITIES).optional(),
  channel: z.enum(CHANNELS).optional().nullable(),
  dueDate: z.string().datetime().optional().nullable(),
  userId: z.string().uuid().optional(),
});

// ─── KPI attribution (weekly logging) ────────────────────────────────────────────
export const contributionSchema = z.object({
  taskId: z.string().uuid(),
  kpiIndicatorId: z.string().uuid(),
  impactDescription: z.string().optional().nullable(),
  metricDelta: z.coerce.number().default(0),
});

export type CreateUserInput = z.infer<typeof createUserSchema>;
export type KpiInput = z.infer<typeof kpiSchema>;
export type CreateTaskInput = z.infer<typeof createTaskSchema>;
export type CompanyInput = z.infer<typeof companySchema>;
export type DepartmentInput = z.infer<typeof departmentSchema>;
export type CompanyFinancialsInput = z.infer<typeof companyFinancialsSchema>;
export type AppraisalCreateInput = z.infer<typeof appraisalCreateSchema>;
export type AppraisalUpdateInput = z.infer<typeof appraisalUpdateSchema>;
export type ChannelInput = z.infer<typeof channelSchema>;
export type PlatformInput = z.infer<typeof platformSchema>;
export type CreatePostInput = z.infer<typeof createPostSchema>;
export type UpdatePostInput = z.infer<typeof updatePostSchema>;
