import { Router } from "express";
import { z } from "zod";
import { requireAuth } from "../../middleware/auth.js";
import { requirePermission } from "../../middleware/rbac.js";
import { validateBody } from "../../middleware/validate.js";
import {
  approveDefinition,
  createDefinition,
  getDefinition,
  listDefinitions,
  listMeasurements,
  requireKpiAuthority,
  reviewMeasurement,
  setTarget,
  submitMeasurement,
} from "./service.js";

export const kpisRouter = Router();
// Deny by default: reading requires kpis.view
kpisRouter.use(requireAuth, requirePermission("kpis.view"));

kpisRouter.get("/", async (_req, res, next) => {
  try {
    res.json({ items: await listDefinitions() });
  } catch (error) {
    next(error);
  }
});

kpisRouter.get("/measurements", async (_req, res, next) => {
  try {
    res.json({ items: await listMeasurements() });
  } catch (error) {
    next(error);
  }
});

/** Create a draft definition (kpis.manage). Approval is a separate authority action. */
kpisRouter.post(
  "/",
  requirePermission("kpis.manage"),
  validateBody(
    z.object({
      code: z.string().min(3).max(50),
      nameAr: z.string().min(3).max(200),
      nameEn: z.string().max(200).nullable().optional(),
      description: z.string().max(2000).nullable().optional(),
      unit: z.enum(["count", "percent", "hours", "score"]),
      direction: z.enum(["higher_better", "lower_better"]),
    }),
  ),
  async (req, res, next) => {
    try {
      const def = await createDefinition(req, req.body);
      res.status(201).json(def);
    } catch (error) {
      next(error);
    }
  },
);

/** AUTHORITY ACTION: draft → active (permission + DEC-04 role trio, server-side). */
kpisRouter.post(
  "/:id/approve",
  requirePermission("kpis.approve"),
  async (req, res, next) => {
    try {
      const id = z.coerce.number().int().positive().parse(req.params.id);
      await approveDefinition(req, id);
      res.json({ ok: true, definition: await getDefinition(id) });
    } catch (error) {
      next(error);
    }
  },
);

/** AUTHORITY ACTION: set/override a period target. */
kpisRouter.post(
  "/:id/targets",
  requirePermission("kpis.approve"),
  validateBody(
    z.object({
      period: z.string().regex(/^\d{4}(-Q[1-4])?$/),
      scopeType: z.enum(["national", "secretariat"]),
      orgUnitId: z.coerce.number().int().positive().nullable().optional(),
      targetValue: z.coerce.number().min(0),
    }),
  ),
  async (req, res, next) => {
    try {
      const id = z.coerce.number().int().positive().parse(req.params.id);
      const b = req.body as { period: string; scopeType: "national" | "secretariat"; orgUnitId?: number | null; targetValue: number };
      await setTarget(req, { definitionId: id, period: b.period, scopeType: b.scopeType, orgUnitId: b.orgUnitId ?? null, targetValue: b.targetValue });
      res.json({ ok: true, definition: await getDefinition(id) });
    } catch (error) {
      next(error);
    }
  },
);

/** Submit a measurement (kpis.manage). */
kpisRouter.post(
  "/measurements",
  requirePermission("kpis.manage"),
  validateBody(
    z.object({
      definitionId: z.coerce.number().int().positive(),
      period: z.string().regex(/^\d{4}(-Q[1-4])?$/),
      scopeType: z.enum(["national", "secretariat"]),
      orgUnitId: z.coerce.number().int().positive().nullable().optional(),
      value: z.coerce.number().min(0),
      evidenceRef: z.string().max(255).nullable().optional(),
    }),
  ),
  async (req, res, next) => {
    try {
      const b = req.body as { definitionId: number; period: string; scopeType: "national" | "secretariat"; orgUnitId?: number | null; value: number; evidenceRef?: string | null };
      await submitMeasurement(req, { ...b, orgUnitId: b.orgUnitId ?? null });
      res.status(201).json({ ok: true });
    } catch (error) {
      next(error);
    }
  },
);

/** AUTHORITY ACTION: approve/reject a submitted measurement. */
kpisRouter.post(
  "/measurements/:id/review",
  requirePermission("kpis.approve"),
  validateBody(
    z.object({
      decision: z.enum(["approved", "rejected"]),
      reviewNote: z.string().max(500).nullable().optional(),
    }),
  ),
  async (req, res, next) => {
    try {
      const id = z.coerce.number().int().positive().parse(req.params.id);
      const b = req.body as { decision: "approved" | "rejected"; reviewNote?: string | null };
      await reviewMeasurement(req, id, b.decision, b.reviewNote ?? null);
      res.json({ ok: true });
    } catch (error) {
      next(error);
    }
  },
);
