import { Router } from 'express';
import { PlannedActivityStatus, Prisma, SystemRole } from '@prisma/client';
import { z } from 'zod';
import { prisma } from '../db';
import { toDateOnly } from '../lib/dates';
import { badRequest, forbidden, notFound } from '../lib/errors';
import { param } from '../lib/params';
import { loadProjectContext, visibleProjectIds } from '../lib/rbac';
import { AuthedRequest, requireAuth } from '../middleware/auth';

function canManageActivity(
  user: { id: string; role: SystemRole },
  assigneeId: string,
  isProjectMember: boolean | undefined
) {
  return (
    user.role === SystemRole.SuperAdmin ||
    Boolean(isProjectMember) ||
    assigneeId === user.id
  );
}

export const activitiesRouter = Router();
activitiesRouter.use(requireAuth);

const typeSchema = z.enum(['todo', 'email', 'call', 'meeting', 'document']);
const statusSchema = z.enum(['planned', 'done']);

function serializeActivity(a: {
  id: string;
  type: string;
  summary: string;
  dueDate: Date | null;
  note: string;
  status: PlannedActivityStatus;
  createdAt: Date;
  updatedAt: Date;
  assigneeId: string;
  taskId: string;
  assignee?: { id: string; name: string; code: string; avatarColor: string } | null;
  task?: {
    id: string;
    code: string;
    title: string;
    projectId: string;
    project?: { id: string; code: string; name: string } | null;
  } | null;
}) {
  return {
    id: a.id,
    type: a.type,
    summary: a.summary,
    dueDate: a.dueDate ? a.dueDate.toISOString().slice(0, 10) : null,
    note: a.note,
    status: a.status,
    assigneeId: a.assigneeId,
    assignee: a.assignee || undefined,
    taskId: a.taskId,
    task: a.task
      ? {
          id: a.task.id,
          code: a.task.code,
          title: a.task.title,
          projectId: a.task.projectId,
          project: a.task.project || undefined,
        }
      : undefined,
    createdAt: a.createdAt.toISOString(),
    updatedAt: a.updatedAt.toISOString(),
  };
}

activitiesRouter.get('/', async (req: AuthedRequest, res, next) => {
  try {
    const user = req.user!;
    const q = String(req.query.q || '').trim();
    const status = String(req.query.status || '').trim();
    const type = String(req.query.type || '').trim();
    const assigneeId = String(req.query.assigneeId || '').trim();
    const projectId = String(req.query.projectId || '').trim();
    const clientId = String(req.query.clientId || '').trim();
    const mine = req.query.mine === '1' || req.query.mine === 'true';
    const dueFrom = String(req.query.dueFrom || '').trim();
    const dueTo = String(req.query.dueTo || '').trim();

    const projectIds = await visibleProjectIds(user);
    const where: Prisma.PlannedActivityWhereInput = {};
    const taskWhere: Prisma.TaskWhereInput = {};

    if (projectIds !== 'all') taskWhere.projectId = { in: projectIds };
    if (projectId) taskWhere.projectId = projectId;
    if (clientId) taskWhere.project = { clientId };

    if (mine) where.assigneeId = user.id;
    else if (assigneeId) where.assigneeId = assigneeId;

    if (status === 'planned' || status === 'done') where.status = status;
    if (type) {
      const parsed = typeSchema.safeParse(type);
      if (parsed.success) where.type = parsed.data;
    }
    if (q) {
      where.OR = [
        { summary: { contains: q, mode: 'insensitive' } },
        { note: { contains: q, mode: 'insensitive' } },
        { task: { is: { title: { contains: q, mode: 'insensitive' } } } },
        { task: { is: { code: { contains: q, mode: 'insensitive' } } } },
      ];
    }
    if (Object.keys(taskWhere).length) where.task = taskWhere;
    if (dueFrom || dueTo) {
      where.dueDate = {};
      if (dueFrom) where.dueDate.gte = toDateOnly(dueFrom) || undefined;
      if (dueTo) where.dueDate.lte = toDateOnly(dueTo) || undefined;
    }

    const rows = await prisma.plannedActivity.findMany({
      where,
      include: {
        assignee: { select: { id: true, name: true, code: true, avatarColor: true } },
        task: {
          select: {
            id: true,
            code: true,
            title: true,
            projectId: true,
            project: { select: { id: true, code: true, name: true } },
          },
        },
      },
      orderBy: [{ status: 'asc' }, { dueDate: 'asc' }, { createdAt: 'desc' }],
      take: 500,
    });

    res.json({ activities: rows.map(serializeActivity) });
  } catch (err) {
    next(err);
  }
});

activitiesRouter.patch('/:id', async (req: AuthedRequest, res, next) => {
  try {
    const user = req.user!;
    const id = param(req.params.id);
    const existing = await prisma.plannedActivity.findUnique({
      where: { id },
      include: { task: { select: { projectId: true } } },
    });
    if (!existing) throw notFound('Activity not found');

    const ctx = await loadProjectContext(user.id, existing.task.projectId);
    if (!canManageActivity(user, existing.assigneeId, ctx.isProjectMember)) {
      throw forbidden('You cannot update this activity');
    }

    const body = z
      .object({
        type: typeSchema.optional(),
        summary: z.string().trim().min(1).optional(),
        dueDate: z.string().nullable().optional(),
        assigneeId: z.string().uuid().optional(),
        note: z.string().optional(),
        status: statusSchema.optional(),
      })
      .parse(req.body);

    const updated = await prisma.plannedActivity.update({
      where: { id },
      data: {
        type: body.type,
        summary: body.summary,
        dueDate: body.dueDate !== undefined ? toDateOnly(body.dueDate) : undefined,
        assigneeId: body.assigneeId,
        note: body.note,
        status: body.status,
      },
      include: {
        assignee: { select: { id: true, name: true, code: true, avatarColor: true } },
        task: {
          select: {
            id: true,
            code: true,
            title: true,
            projectId: true,
            project: { select: { id: true, code: true, name: true } },
          },
        },
      },
    });
    res.json({ activity: serializeActivity(updated) });
  } catch (err) {
    next(err);
  }
});

activitiesRouter.post('/:id/done', async (req: AuthedRequest, res, next) => {
  try {
    const user = req.user!;
    const id = param(req.params.id);
    const existing = await prisma.plannedActivity.findUnique({
      where: { id },
      include: { task: { select: { projectId: true } } },
    });
    if (!existing) throw notFound('Activity not found');
    const ctx = await loadProjectContext(user.id, existing.task.projectId);
    if (!canManageActivity(user, existing.assigneeId, ctx.isProjectMember)) {
      throw forbidden('You cannot update this activity');
    }

    const updated = await prisma.plannedActivity.update({
      where: { id },
      data: { status: 'done' },
      include: {
        assignee: { select: { id: true, name: true, code: true, avatarColor: true } },
        task: {
          select: {
            id: true,
            code: true,
            title: true,
            projectId: true,
            project: { select: { id: true, code: true, name: true } },
          },
        },
      },
    });
    res.json({ activity: serializeActivity(updated) });
  } catch (err) {
    next(err);
  }
});

activitiesRouter.post('/:id/reopen', async (req: AuthedRequest, res, next) => {
  try {
    const user = req.user!;
    const id = param(req.params.id);
    const existing = await prisma.plannedActivity.findUnique({
      where: { id },
      include: { task: { select: { projectId: true } } },
    });
    if (!existing) throw notFound('Activity not found');
    if (existing.status !== 'done') throw badRequest('Activity is not done');
    const ctx = await loadProjectContext(user.id, existing.task.projectId);
    if (!canManageActivity(user, existing.assigneeId, ctx.isProjectMember)) {
      throw forbidden('You cannot update this activity');
    }

    const updated = await prisma.plannedActivity.update({
      where: { id },
      data: { status: 'planned' },
      include: {
        assignee: { select: { id: true, name: true, code: true, avatarColor: true } },
        task: {
          select: {
            id: true,
            code: true,
            title: true,
            projectId: true,
            project: { select: { id: true, code: true, name: true } },
          },
        },
      },
    });
    res.json({ activity: serializeActivity(updated) });
  } catch (err) {
    next(err);
  }
});
