import { Router } from 'express';
import { Prisma } from '@prisma/client';
import { prisma } from '../lib/prisma';
import { resolveTaskActorId } from '../lib/resolveUser';
import { formatMilestone, milestoneInclude, parseMemberIds } from '../lib/taskQuery';
import { resolveMilestonePeriodPayload } from '../lib/milestonePeriods';
import { AppError } from '../middleware/errorHandler';

const router = Router();

function normalizeLinks(links: unknown): string | null {
  if (!links) return null;
  if (typeof links === 'string') {
    try {
      const parsed = JSON.parse(links);
      if (Array.isArray(parsed) && parsed.length === 0) return null;
      return links;
    } catch {
      return null;
    }
  }
  if (Array.isArray(links)) {
    return links.length ? JSON.stringify(links) : null;
  }
  return null;
}

function clampProgress(value: unknown): number {
  const n = Number(value);
  if (Number.isNaN(n)) return 0;
  return Math.min(100, Math.max(0, Math.round(n)));
}

async function syncMilestoneMembers(
  milestoneId: string,
  pmMemberId: string | null | undefined,
  assigneeMemberIds: string[] | undefined,
) {
  if (pmMemberId !== undefined) {
    await prisma.milestone.update({
      where: { id: milestoneId },
      data: { pmMemberId: pmMemberId || null },
    });
  }

  if (assigneeMemberIds !== undefined) {
    await prisma.milestoneAssignee.deleteMany({ where: { milestoneId } });
    const ids = [...new Set(assigneeMemberIds.filter(Boolean))];
    if (ids.length > 0) {
      await prisma.milestoneAssignee.createMany({
        data: ids.map((memberId) => ({ milestoneId, memberId })),
      });
    }
  }
}

async function loadMilestone(id: string) {
  const milestone = await prisma.milestone.findUnique({
    where: { id },
    include: milestoneInclude,
  });
  if (!milestone) throw new AppError(404, '단계를 찾을 수 없습니다.');
  return formatMilestone(milestone);
}

// GET /api/milestones/:taskId
router.get('/:taskId', async (req, res, next) => {
  try {
    const milestones = await prisma.milestone.findMany({
      where: { taskId: req.params.taskId },
      orderBy: { order: 'asc' },
      include: milestoneInclude,
    });
    res.json(milestones.map((m) => formatMilestone(m)));
  } catch (err) {
    next(err);
  }
});

// POST /api/milestones/:taskId
router.post('/:taskId', async (req, res, next) => {
  try {
    const taskId = req.params.taskId;
    const body = req.body as Record<string, unknown>;
    const { title, subtitle, description, startDate, dueDate, feedback, links, progress } = body;
    const assigneeMemberIds = parseMemberIds(body);
    const pmMemberId =
      body.pmMemberId !== undefined ? String(body.pmMemberId || '') || null : undefined;

    if (!title?.toString().trim()) throw new AppError(400, '단계 제목은 필수입니다.');

    const count = await prisma.milestone.count({ where: { taskId } });
    const periodPayload = resolveMilestonePeriodPayload(body);

    const milestone = await prisma.milestone.create({
      data: {
        taskId,
        title: String(title).trim(),
        subtitle: subtitle !== undefined ? String(subtitle || '').trim() || null : null,
        description: description?.toString().trim() || null,
        startDate:
          periodPayload.startDate !== undefined
            ? periodPayload.startDate
            : startDate
              ? new Date(String(startDate))
              : null,
        dueDate:
          periodPayload.dueDate !== undefined
            ? periodPayload.dueDate
            : dueDate
              ? new Date(String(dueDate))
              : null,
        periodEntries:
          periodPayload.periodEntries !== undefined
            ? (periodPayload.periodEntries as Prisma.InputJsonValue)
            : undefined,
        progress: progress !== undefined ? clampProgress(progress) : 0,
        links: normalizeLinks(links),
        order: count,
        ...(pmMemberId !== undefined ? { pmMemberId } : {}),
      },
    });

    await syncMilestoneMembers(milestone.id, pmMemberId, assigneeMemberIds);

    if (feedback?.toString().trim()) {
      const updatedBy = await resolveTaskActorId(taskId);
      await prisma.taskDetail.create({
        data: {
          taskId,
          milestoneId: milestone.id,
          content: feedback.toString().trim(),
          updatedBy,
        },
      });
    }

    res.status(201).json(await loadMilestone(milestone.id));
  } catch (err) {
    next(err);
  }
});

// PATCH /api/milestones/item/:id
router.patch('/item/:id', async (req, res, next) => {
  try {
    const body = req.body as Record<string, unknown>;
    const { title, subtitle, description, startDate, dueDate, feedback, links, progress, completed, order } =
      body;
    const assigneeMemberIds = parseMemberIds(body);
    const pmMemberId =
      body.pmMemberId !== undefined ? String(body.pmMemberId || '') || null : undefined;

    const existing = await prisma.milestone.findUnique({ where: { id: req.params.id } });
    if (!existing) throw new AppError(404, '단계를 찾을 수 없습니다.');

    const periodPayload = resolveMilestonePeriodPayload(body);

    const milestone = await prisma.milestone.update({
      where: { id: req.params.id },
      data: {
        ...(title !== undefined && { title: String(title).trim() }),
        ...(subtitle !== undefined && { subtitle: String(subtitle || '').trim() || null }),
        ...(description !== undefined && { description: description ? String(description).trim() : null }),
        ...(periodPayload.startDate !== undefined && { startDate: periodPayload.startDate }),
        ...(periodPayload.dueDate !== undefined && { dueDate: periodPayload.dueDate }),
        ...(periodPayload.periodEntries !== undefined && {
          periodEntries: periodPayload.periodEntries as Prisma.InputJsonValue,
        }),
        ...(periodPayload.startDate === undefined &&
          startDate !== undefined && { startDate: startDate ? new Date(String(startDate)) : null }),
        ...(periodPayload.dueDate === undefined &&
          dueDate !== undefined && { dueDate: dueDate ? new Date(String(dueDate)) : null }),
        ...(progress !== undefined && { progress: clampProgress(progress) }),
        ...(links !== undefined && { links: normalizeLinks(links) }),
        ...(order !== undefined && { order: Number(order) }),
        ...(completed !== undefined && {
          completedAt: completed ? new Date() : null,
          ...(completed && { progress: 100 }),
        }),
        ...(pmMemberId !== undefined ? { pmMemberId } : {}),
      },
    });

    await syncMilestoneMembers(milestone.id, pmMemberId, assigneeMemberIds);

    if (typeof feedback === 'string' && feedback.trim()) {
      const updatedBy = await resolveTaskActorId(existing.taskId);
      await prisma.taskDetail.create({
        data: {
          taskId: existing.taskId,
          milestoneId: milestone.id,
          content: feedback.trim(),
          updatedBy,
        },
      });
    }

    res.json(await loadMilestone(milestone.id));
  } catch (err) {
    next(err);
  }
});

// DELETE /api/milestones/item/:id
router.delete('/item/:id', async (req, res, next) => {
  try {
    await prisma.milestone.delete({ where: { id: req.params.id } });
    res.status(204).send();
  } catch (err) {
    next(err);
  }
});

export default router;
