import { Router } from 'express';
import { authenticate, authorize } from '../../shared/middleware/auth';
import prisma from '../../config/database';
import { ResponseFormatter } from '../../shared/utils/responseFormatter';
import { AuditLogger } from '../../shared/utils/auditLogger';
import { AppError } from '../../shared/middleware/errorHandler';

const router = Router();
router.use(authenticate);

// ============================================================
// Helpers
// ============================================================

// Convert a "YYYY-MM-DD" or ISO string to a Date at UTC midnight.
// Prisma's @db.Date columns require a real DateTime; a bare "YYYY-MM-DD"
// in JSON gets parsed to "Invalid data" by the validator.
const toDate = (value: unknown): Date | null | undefined => {
  if (value === null) return null;
  if (value === undefined || value === '') return undefined;
  if (value instanceof Date) return value;
  if (typeof value !== 'string') return undefined;
  // Already ISO?
  if (value.includes('T')) {
    const d = new Date(value);
    return isNaN(d.getTime()) ? undefined : d;
  }
  // Plain "YYYY-MM-DD"
  const d = new Date(`${value}T00:00:00Z`);
  return isNaN(d.getTime()) ? undefined : d;
};

// Drop empty strings, normalize known date/number fields, and only keep keys
// that actually exist on the Prisma Task model. Anything extra (e.g. `tags`)
// would otherwise crash Prisma with "Unknown argument".
const sanitizeTaskBody = (body: any, opts: { isUpdate?: boolean } = {}) => {
  const out: any = {};

  const setIfPresent = (key: string, value: any) => {
    if (value === undefined) return;
    out[key] = value;
  };

  // Strings: collapse '' -> null (update) or skip (create)
  const strField = (key: string) => {
    if (!(key in body)) return;
    const v = body[key];
    if (v === '' || v === null) {
      if (opts.isUpdate) out[key] = null;
      return;
    }
    if (typeof v === 'string') out[key] = v.trim();
  };

  strField('title');
  strField('description');
  strField('projectId');
  strField('phaseId');
  strField('parentId');
  strField('assignedToId');

  if ('status' in body && body.status) out.status = String(body.status);
  if ('priority' in body && body.priority) out.priority = String(body.priority);

  if ('progress' in body) {
    const n = Number(body.progress);
    if (!Number.isNaN(n)) out.progress = Math.max(0, Math.min(100, Math.round(n)));
  }

  if ('startDate' in body) {
    const d = toDate(body.startDate);
    if (d !== undefined) out.startDate = d;
  }
  if ('dueDate' in body) {
    const d = toDate(body.dueDate);
    if (d !== undefined) out.dueDate = d;
  }
  if ('completedDate' in body) {
    const d = toDate(body.completedDate);
    if (d !== undefined) out.completedDate = d;
  }

  if ('estimatedHours' in body) {
    const v = body.estimatedHours;
    if (v === '' || v === null) {
      if (opts.isUpdate) out.estimatedHours = null;
    } else {
      const n = Number(v);
      if (!Number.isNaN(n) && n >= 0) out.estimatedHours = n;
    }
  }
  if ('actualHours' in body) {
    const v = body.actualHours;
    if (v === '' || v === null) {
      if (opts.isUpdate) out.actualHours = null;
    } else {
      const n = Number(v);
      if (!Number.isNaN(n) && n >= 0) out.actualHours = n;
    }
  }

  // dependencies is Json? in the schema
  if ('dependencies' in body) out.dependencies = body.dependencies;

  return out;
};

const sanitizeProjectBody = (body: any, opts: { isUpdate?: boolean } = {}) => {
  const out: any = {};

  const strField = (key: string) => {
    if (!(key in body)) return;
    const v = body[key];
    if (v === '' || v === null) {
      if (opts.isUpdate) out[key] = null;
      return;
    }
    if (typeof v === 'string') out[key] = v.trim();
  };

  // Required-ish strings
  if ('name' in body && body.name) out.name = String(body.name).trim();
  if ('type' in body && body.type) out.type = String(body.type);
  strField('description');
  strField('dealId');
  strField('clientId');

  if ('status' in body && body.status) out.status = String(body.status);
  if ('priority' in body && body.priority) out.priority = String(body.priority);

  if ('progress' in body) {
    const n = Number(body.progress);
    if (!Number.isNaN(n)) out.progress = Math.max(0, Math.min(100, Math.round(n)));
  }

  if ('startDate' in body) {
    const d = toDate(body.startDate);
    if (d !== undefined) out.startDate = d;
  }
  if ('endDate' in body) {
    const d = toDate(body.endDate);
    if (d !== undefined) out.endDate = d;
  }
  if ('actualEndDate' in body) {
    const d = toDate(body.actualEndDate);
    if (d !== undefined) out.actualEndDate = d;
  }

  const numField = (key: string) => {
    if (!(key in body)) return;
    const v = body[key];
    if (v === '' || v === null) {
      if (opts.isUpdate) out[key] = null;
      return;
    }
    const n = Number(v);
    if (!Number.isNaN(n)) out[key] = n;
  };
  numField('estimatedCost');
  numField('actualCost');

  return out;
};

// ============================================================
// Projects
// ============================================================

router.get('/projects', authorize('projects', 'projects', 'read'), async (req, res, next) => {
  try {
    const projects = await prisma.project.findMany({
      include: {
        phases: true,
        tasks: { take: 10 },
        members: { include: { user: true } },
      },
      orderBy: { createdAt: 'desc' },
    });
    res.json(ResponseFormatter.success(projects));
  } catch (error) {
    next(error);
  }
});

router.get('/projects/:id', authorize('projects', 'projects', 'read'), async (req, res, next) => {
  try {
    const project = await prisma.project.findUnique({
      where: { id: req.params.id },
      include: {
        phases: { include: { tasks: true } },
        tasks: true,
        members: { include: { user: { include: { employee: true } } } },
        expenses: true,
        attachments: true,
        notes: true,
      },
    });
    if (!project) {
      throw new AppError(404, 'المشروع غير موجود - Project not found');
    }
    res.json(ResponseFormatter.success(project));
  } catch (error) {
    next(error);
  }
});

router.post('/projects', authorize('projects', 'projects', 'create'), async (req, res, next) => {
  try {
    const data = sanitizeProjectBody(req.body, { isUpdate: false });

    if (!data.name) {
      throw new AppError(400, 'اسم المشروع مطلوب - Project name is required');
    }
    if (!data.type) {
      throw new AppError(400, 'نوع المشروع مطلوب - Project type is required');
    }
    if (!data.startDate) {
      throw new AppError(400, 'تاريخ البدء مطلوب - Start date is required');
    }

    const projectNumber = `PRJ-${new Date().getFullYear()}-${Date.now().toString().slice(-6)}`;
    const project = await prisma.project.create({
      data: { ...data, projectNumber },
    });

    await AuditLogger.log({
      entityType: 'PROJECT',
      entityId: project.id,
      action: 'CREATE',
      userId: (req as any).user.id,
    });

    res.status(201).json(ResponseFormatter.success(project));
  } catch (error) {
    next(error);
  }
});

router.put('/projects/:id', authorize('projects', 'projects', 'update'), async (req, res, next) => {
  try {
    const data = sanitizeProjectBody(req.body, { isUpdate: true });
    const project = await prisma.project.update({
      where: { id: req.params.id },
      data,
    });

    await AuditLogger.log({
      entityType: 'PROJECT',
      entityId: project.id,
      action: 'UPDATE',
      userId: (req as any).user.id,
    });

    res.json(ResponseFormatter.success(project));
  } catch (error) {
    next(error);
  }
});

router.delete(
  '/projects/:id',
  authorize('projects', 'projects', 'delete'),
  async (req, res, next) => {
    try {
      // Block delete when the project still has tasks / phases / etc.
      const counts = await prisma.project.findUnique({
        where: { id: req.params.id },
        select: {
          _count: {
            select: {
              tasks: true,
              phases: true,
              members: true,
              expenses: true,
            },
          },
        },
      });

      if (!counts) {
        throw new AppError(404, 'المشروع غير موجود - Project not found');
      }

      const c = counts._count;
      if (c.tasks > 0 || c.phases > 0 || c.expenses > 0) {
        throw new AppError(
          409,
          `لا يمكن حذف المشروع - يحتوي على ${c.tasks} مهمة، ${c.phases} مرحلة، ${c.expenses} مصروف. احذفها أولاً.`,
        );
      }

      // Detach members (safe - they're just join rows) then delete
      if (c.members > 0) {
        await prisma.projectMember.deleteMany({ where: { projectId: req.params.id } });
      }

      await prisma.project.delete({ where: { id: req.params.id } });

      await AuditLogger.log({
        entityType: 'PROJECT',
        entityId: req.params.id,
        action: 'DELETE',
        userId: (req as any).user.id,
      });

      res.json(ResponseFormatter.success({ id: req.params.id }, 'تم حذف المشروع - Project deleted'));
    } catch (error) {
      next(error);
    }
  },
);

// ============================================================
// Tasks
// ============================================================

router.get('/tasks', authorize('projects', 'tasks', 'read'), async (req, res, next) => {
  try {
    const page = Math.max(1, parseInt(String(req.query.page || '1'), 10) || 1);
    const pageSize = Math.min(
      100,
      Math.max(1, parseInt(String(req.query.pageSize || '20'), 10) || 20),
    );

    const where: any = {};
    if (req.query.projectId) where.projectId = req.query.projectId;
    if (req.query.assignedToId) where.assignedToId = req.query.assignedToId;
    if (req.query.status) where.status = req.query.status;
    if (req.query.priority) where.priority = req.query.priority;

    if (req.query.search) {
      const q = String(req.query.search);
      where.OR = [
        { title: { contains: q, mode: 'insensitive' } },
        { description: { contains: q, mode: 'insensitive' } },
        { taskNumber: { contains: q, mode: 'insensitive' } },
      ];
    }

    const [tasks, total] = await Promise.all([
      prisma.task.findMany({
        where,
        include: {
          project: true,
          assignedTo: { select: { id: true, email: true, username: true } },
        },
        orderBy: { createdAt: 'desc' },
        skip: (page - 1) * pageSize,
        take: pageSize,
      }),
      prisma.task.count({ where }),
    ]);

    res.json(ResponseFormatter.paginated(tasks, total, page, pageSize));
  } catch (error) {
    next(error);
  }
});

router.get('/tasks/:id', authorize('projects', 'tasks', 'read'), async (req, res, next) => {
  try {
    const task = await prisma.task.findUnique({
      where: { id: req.params.id },
      include: {
        project: true,
        assignedTo: { select: { id: true, email: true, username: true } },
        phase: true,
        parent: true,
        children: true,
      },
    });
    if (!task) {
      throw new AppError(404, 'المهمة غير موجودة - Task not found');
    }
    res.json(ResponseFormatter.success(task));
  } catch (error) {
    next(error);
  }
});

router.post('/tasks', authorize('projects', 'tasks', 'create'), async (req, res, next) => {
  try {
    const data = sanitizeTaskBody(req.body, { isUpdate: false });

    if (!data.title) {
      throw new AppError(400, 'عنوان المهمة مطلوب - Task title is required');
    }

    const taskNumber = `TSK-${new Date().getFullYear()}-${Date.now().toString().slice(-6)}`;
    const task = await prisma.task.create({
      data: { ...data, taskNumber },
      include: { project: true, assignedTo: true },
    });

    // Create notification for assigned user
    if (task.assignedToId) {
      await prisma.notification.create({
        data: {
          userId: task.assignedToId,
          type: 'TASK_ASSIGNED',
          title: 'مهمة جديدة - New Task Assigned',
          message: `تم تعيينك لمهمة: ${task.title}`,
          entityType: 'TASK',
          entityId: task.id,
        },
      });
    }

    await AuditLogger.log({
      entityType: 'TASK',
      entityId: task.id,
      action: 'CREATE',
      userId: (req as any).user.id,
    });

    res.status(201).json(ResponseFormatter.success(task));
  } catch (error) {
    next(error);
  }
});

router.put('/tasks/:id', authorize('projects', 'tasks', 'update'), async (req, res, next) => {
  try {
    const data = sanitizeTaskBody(req.body, { isUpdate: true });
    const task = await prisma.task.update({
      where: { id: req.params.id },
      data,
      include: { project: true, assignedTo: true },
    });

    await AuditLogger.log({
      entityType: 'TASK',
      entityId: task.id,
      action: 'UPDATE',
      userId: (req as any).user.id,
    });

    res.json(ResponseFormatter.success(task));
  } catch (error) {
    next(error);
  }
});

router.delete('/tasks/:id', authorize('projects', 'tasks', 'delete'), async (req, res, next) => {
  try {
    await prisma.task.delete({ where: { id: req.params.id } });

    await AuditLogger.log({
      entityType: 'TASK',
      entityId: req.params.id,
      action: 'DELETE',
      userId: (req as any).user.id,
    });

    res.json(ResponseFormatter.success({ id: req.params.id }, 'تم حذف المهمة - Task deleted'));
  } catch (error) {
    next(error);
  }
});

export default router;
