import { Router } from "express";
import { z } from "zod";
import { requireAuth } from "../../middleware/auth.js";
import { governorateScope, requirePermission } from "../../middleware/rbac.js";
import { validateBody } from "../../middleware/validate.js";
import { query } from "../../db/client.js";
import { notFound } from "../../lib/errors.js";
import {
  archiveUnit,
  closePosition,
  createPosition,
  createUnit,
  getUnit,
  listPositions,
  listUnits,
} from "./service.js";

export const structureRouter = Router();
// Deny by default: reading requires structure.view; mutations additionally structure.manage
structureRouter.use(requireAuth, requirePermission("structure.view"));

structureRouter.get("/governorates", async (req, res, next) => {
  try {
    // Scoped callers get only their own governorates (nothing else is selectable for them)
    const scope = governorateScope(req);
    const params: unknown[] = [];
    let where = "";
    if (scope !== null) {
      if (scope.length === 0) {
        res.json({ items: [] });
        return;
      }
      params.push(scope);
      where = `WHERE id = ANY($1::int[])`;
    }
    const rows = await query(`SELECT id, name_ar FROM governorates ${where} ORDER BY id`, params);
    res.json({ items: rows.rows });
  } catch (error) {
    next(error);
  }
});

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

structureRouter.post(
  "/units",
  requirePermission("structure.manage"),
  validateBody(
    z.object({
      unitKind: z.enum(["national", "governorate"]),
      governorateId: z.coerce.number().int().min(1).max(27).nullable().optional(),
      nameAr: z.string().min(3).max(200),
      formationDecisionRef: z.string().min(3).max(255), // G-1: every unit is a documented decision
      notes: z.string().max(2000).nullable().optional(),
    }),
  ),
  async (req, res, next) => {
    try {
      const unit = await createUnit(req, req.body);
      res.status(201).json(unit);
    } catch (error) {
      next(error);
    }
  },
);

structureRouter.get("/units/:id", async (req, res, next) => {
  try {
    const id = z.coerce.number().int().positive().parse(req.params.id);
    const unit = await getUnit(req, id);
    if (!unit) throw notFound("الوحدة غير موجودة أو خارج نطاقك");
    res.json({ unit, positions: await listPositions(req, id) });
  } catch (error) {
    next(error);
  }
});

structureRouter.post(
  "/units/:id/positions",
  requirePermission("structure.manage"),
  validateBody(
    z.object({
      memberUuid: z.string().uuid("معرف العضو غير صالح").nullable().optional(),
      titleAr: z.string().min(2).max(150),
      appointmentDecisionRef: z.string().min(3).max(255), // G-1: documented appointment required
      decidedBy: z.string().max(150).nullable().optional(),
      startedOn: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional(),
    }),
  ),
  async (req, res, next) => {
    try {
      const id = z.coerce.number().int().positive().parse(req.params.id);
      const position = await createPosition(req, { ...req.body, unitId: id });
      res.status(201).json(position);
    } catch (error) {
      next(error);
    }
  },
);

structureRouter.post(
  "/units/:id/archive",
  requirePermission("structure.manage"),
  validateBody(z.object({ reason: z.string().min(5).max(500) })),
  async (req, res, next) => {
    try {
      const id = z.coerce.number().int().positive().parse(req.params.id);
      await archiveUnit(req, id, req.body.reason);
      res.json({ ok: true });
    } catch (error) {
      next(error);
    }
  },
);

structureRouter.post(
  "/positions/:id/close",
  requirePermission("structure.manage"),
  validateBody(
    z.object({
      endedOn: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
      endReason: z.string().min(5).max(500),
    }),
  ),
  async (req, res, next) => {
    try {
      const id = z.coerce.number().int().positive().parse(req.params.id);
      await closePosition(req, id, req.body);
      res.json({ ok: true });
    } catch (error) {
      next(error);
    }
  },
);
