const express = require('express');
const axios = require('axios');
const NodeCache = require('node-cache');

const router = express.Router();
const cache = new NodeCache({ stdTTL: parseInt(process.env.CACHE_TTL) || 300 });

const CU = axios.create({
  baseURL: 'https://api.clickup.com/api/v2',
  headers: { Authorization: process.env.CLICKUP_API_TOKEN },
});

function normalizeStatus(raw = '') {
  const s = raw.toLowerCase();
  if (s.includes('progress') || s.includes('doing') || s.includes('active')) return 'in_progress';
  if (s.includes('review') || s.includes('testing') || s.includes('qa')) return 'in_review';
  if (s.includes('done') || s.includes('complete') || s.includes('closed') || s.includes('resolved')) return 'done';
  return 'todo';
}

function normalizeTask(t) {
  return {
    id: t.id,
    name: t.name,
    status: normalizeStatus(t.status?.status),
    statusRaw: t.status?.status || '',
    statusColor: t.status?.color || '#888',
    priority: t.priority?.priority || 'normal',
    priorityColor: t.priority?.color || '#888',
    assignees: (t.assignees || []).map(a => ({
      id: String(a.id), name: a.username, email: a.email, avatar: a.profilePicture,
    })),
    dueDate: t.due_date ? new Date(parseInt(t.due_date)).toISOString().split('T')[0] : null,
    timeEstimate: t.time_estimate ? Math.round(t.time_estimate / 3600000 * 10) / 10 : null, // ms → hours
    timeSpent: t.time_spent ? Math.round(t.time_spent / 3600000 * 10) / 10 : null,          // ms → hours
    updatedAt: t.date_updated ? new Date(parseInt(t.date_updated)).toISOString() : null,
    url: t.url,
    listId: t.list?.id || '',
    listName: t.list?.name || '',
    folderId: t.folder?.id || '',
    folderName: t.folder?.name || '',
    spaceName: t.space?.name || '',
    tags: (t.tags || []).map(tag => tag.name),
  };
}

function groupByStatus(tasks) {
  return {
    todo:       tasks.filter(t => t.status === 'todo'),
    inProgress: tasks.filter(t => t.status === 'in_progress'),
    inReview:   tasks.filter(t => t.status === 'in_review'),
    done:       tasks.filter(t => t.status === 'done'),
    total:      tasks.length,
  };
}

async function resolveListIds(folderIds, listIds, spaceIds) {
  const targetListIds = new Set();

  if (listIds) listIds.split(',').filter(Boolean).forEach(id => targetListIds.add(id.trim()));

  if (folderIds) {
    await Promise.all(folderIds.split(',').filter(Boolean).map(async folderId => {
      try {
        const { data } = await CU.get(`/folder/${folderId.trim()}/list?archived=false`);
        data.lists.forEach(l => targetListIds.add(l.id));
      } catch (_) {}
    }));
  }

  if (targetListIds.size === 0) {
    const spaceIdArr = spaceIds
      ? spaceIds.split(',').filter(Boolean).map(s => s.trim())
      : (process.env.CLICKUP_SPACE_IDS ? process.env.CLICKUP_SPACE_IDS.split(',').filter(Boolean) : []);

    let resolvedSpaces = spaceIdArr;
    if (resolvedSpaces.length === 0) {
      const { data } = await CU.get(`/team/${process.env.CLICKUP_TEAM_ID}/space?archived=false`);
      resolvedSpaces = data.spaces.map(s => s.id);
    }
    await Promise.all(resolvedSpaces.map(async spaceId => {
      const [foldersResp, listsResp] = await Promise.all([
        CU.get(`/space/${spaceId}/folder?archived=false`),
        CU.get(`/space/${spaceId}/list?archived=false`),
      ]);
      foldersResp.data.folders.forEach(f => f.lists.forEach(l => targetListIds.add(l.id)));
      listsResp.data.lists.forEach(l => targetListIds.add(l.id));
    }));
  }

  return targetListIds;
}

// ── GET /api/clickup/spaces ───────────────────────────────────
router.get('/spaces', async (req, res, next) => {
  const cacheKey = 'cu_spaces';
  const cached = cache.get(cacheKey);
  if (cached) return res.json(cached);
  try {
    const { data } = await CU.get(`/team/${process.env.CLICKUP_TEAM_ID}/space?archived=false`);
    const spaces = data.spaces.map(s => ({ id: s.id, name: s.name, color: s.color }));
    cache.set(cacheKey, spaces);
    res.json(spaces);
  } catch (err) { next(err); }
});

// ── GET /api/clickup/folders ──────────────────────────────────
router.get('/folders', async (req, res, next) => {
  const cacheKey = 'cu_folders';
  const cached = cache.get(cacheKey);
  if (cached) return res.json(cached);
  try {
    const { data: spaceData } = await CU.get(`/team/${process.env.CLICKUP_TEAM_ID}/space?archived=false`);
    const result = [];
    await Promise.all(spaceData.spaces.map(async space => {
      const [foldersResp, listsResp] = await Promise.all([
        CU.get(`/space/${space.id}/folder?archived=false`),
        CU.get(`/space/${space.id}/list?archived=false`),
      ]);
      foldersResp.data.folders.forEach(f => result.push({
        id: f.id, name: f.name, spaceName: space.name, spaceId: space.id, type: 'folder',
        lists: f.lists.map(l => ({ id: l.id, name: l.name })),
      }));
      listsResp.data.lists.forEach(l => result.push({
        id: l.id, name: l.name, spaceName: space.name, spaceId: space.id, type: 'list',
        lists: [{ id: l.id, name: l.name }],
      }));
    }));
    cache.set(cacheKey, result);
    res.json(result);
  } catch (err) { next(err); }
});

// ── GET /api/clickup/members ──────────────────────────────────
// Returns all team members
router.get('/members', async (req, res, next) => {
  const cacheKey = 'cu_members';
  const cached = cache.get(cacheKey);
  if (cached) return res.json(cached);
  try {
    const { data } = await CU.get(`/team/${process.env.CLICKUP_TEAM_ID}`);
    const members = (data.team?.members || []).map(m => ({
      id: String(m.user.id),
      name: m.user.username,
      email: m.user.email,
      avatar: m.user.profilePicture,
      role: m.user.role,
    }));
    cache.set(cacheKey, members);
    res.json(members);
  } catch (err) { next(err); }
});

// ── GET /api/clickup/tasks ────────────────────────────────────
router.get('/tasks', async (req, res, next) => {
  const { folderIds, listIds, spaceIds, page = 0 } = req.query;
  const cacheKey = `cu_tasks_f${folderIds}_l${listIds}_s${spaceIds}_p${page}`;
  const cached = cache.get(cacheKey);
  if (cached) return res.json(cached);
  try {
    const targetListIds = await resolveListIds(folderIds, listIds, spaceIds);
    let allTasks = [];
    await Promise.all([...targetListIds].map(async listId => {
      try {
        const { data } = await CU.get(`/list/${listId}/task`, {
          params: { page, include_closed: true, subtasks: false, order_by: 'updated', reverse: true },
        });
        allTasks.push(...data.tasks);
      } catch (_) {}
    }));
    const normalized = allTasks.map(normalizeTask);
    const grouped = groupByStatus(normalized);
    cache.set(cacheKey, grouped);
    res.json(grouped);
  } catch (err) { next(err); }
});

// ── GET /api/clickup/team-overview ───────────────────────────
// Returns: members list, tasks per member, time per member,
//          project/list progress, overdue counts.
// Query: folderIds, listIds, spaceIds (same filters as /tasks)
router.get('/team-overview', async (req, res, next) => {
  const { folderIds, listIds, spaceIds } = req.query;
  const cacheKey = `cu_team_f${folderIds}_l${listIds}_s${spaceIds}`;
  const cached = cache.get(cacheKey);
  if (cached) return res.json(cached);

  try {
    const teamId = process.env.CLICKUP_TEAM_ID;

    // Fetch members + tasks + time tracking in parallel
    const targetListIds = await resolveListIds(folderIds, listIds, spaceIds);

    const [membersResp, ...taskResps] = await Promise.all([
      CU.get(`/team/${teamId}`),
      ...[...targetListIds].map(listId =>
        CU.get(`/list/${listId}/task`, {
          params: { include_closed: true, subtasks: false, order_by: 'updated', reverse: true },
        }).catch(() => ({ data: { tasks: [] } }))
      ),
    ]);

    const members = (membersResp.data.team?.members || []).map(m => ({
      id: String(m.user.id),
      name: m.user.username,
      email: m.user.email,
      avatar: m.user.profilePicture,
    }));

    const allTasks = taskResps.flatMap(r => r.data.tasks || []).map(normalizeTask);

    // ── Per-member stats ──────────────────────────────────────
    const memberMap = {};
    members.forEach(m => {
      memberMap[m.id] = {
        ...m,
        tasks: [],
        totalTasks: 0,
        doneTasks: 0,
        inProgressTasks: 0,
        overdueTasks: 0,
        timeEstimateHours: 0,
        timeSpentHours: 0,
      };
    });

    allTasks.forEach(t => {
      t.assignees.forEach(a => {
        if (!memberMap[a.id]) {
          memberMap[a.id] = {
            ...a, tasks: [], totalTasks: 0, doneTasks: 0,
            inProgressTasks: 0, overdueTasks: 0,
            timeEstimateHours: 0, timeSpentHours: 0,
          };
        }
        const m = memberMap[a.id];
        m.tasks.push(t);
        m.totalTasks++;
        if (t.status === 'done') m.doneTasks++;
        if (t.status === 'in_progress') m.inProgressTasks++;
        if (t.dueDate && t.status !== 'done' && new Date(t.dueDate) < new Date()) m.overdueTasks++;
        if (t.timeEstimate) m.timeEstimateHours += t.timeEstimate;
        if (t.timeSpent)    m.timeSpentHours    += t.timeSpent;
      });
    });

    // Round time values
    Object.values(memberMap).forEach(m => {
      m.timeEstimateHours = Math.round(m.timeEstimateHours * 10) / 10;
      m.timeSpentHours    = Math.round(m.timeSpentHours    * 10) / 10;
      m.completionRate    = m.totalTasks > 0 ? Math.round((m.doneTasks / m.totalTasks) * 100) : 0;
      delete m.tasks; // don't send full task list in this endpoint
    });

    // ── Per-project (list/folder) progress ───────────────────
    const projectMap = {};
    allTasks.forEach(t => {
      const key = t.folderId || t.listId;
      const name = t.folderName || t.listName || 'Unnamed';
      if (!projectMap[key]) {
        projectMap[key] = {
          id: key,
          name,
          spaceName: t.spaceName,
          total: 0, done: 0, inProgress: 0, inReview: 0, todo: 0,
          timeEstimateHours: 0, timeSpentHours: 0,
          overdue: 0,
        };
      }
      const p = projectMap[key];
      p.total++;
      if (t.status === 'done')        p.done++;
      else if (t.status === 'in_progress') p.inProgress++;
      else if (t.status === 'in_review')   p.inReview++;
      else                                  p.todo++;
      if (t.timeEstimate) p.timeEstimateHours += t.timeEstimate;
      if (t.timeSpent)    p.timeSpentHours    += t.timeSpent;
      if (t.dueDate && t.status !== 'done' && new Date(t.dueDate) < new Date()) p.overdue++;
    });

    Object.values(projectMap).forEach(p => {
      p.pctDone = p.total > 0 ? Math.round((p.done / p.total) * 100) : 0;
      p.timeEstimateHours = Math.round(p.timeEstimateHours * 10) / 10;
      p.timeSpentHours    = Math.round(p.timeSpentHours    * 10) / 10;
    });

    // ── Time tracking via ClickUp time entries API ────────────
    // Fetch last 30 days of time entries for the team
    let timeEntries = [];
    try {
      const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1000;
      const { data: teData } = await CU.get(`/team/${teamId}/time_entries`, {
        params: { start_date: thirtyDaysAgo, end_date: Date.now() },
      });
      timeEntries = (teData.data || []).map(e => ({
        id: e.id,
        userId: String(e.user?.id),
        userName: e.user?.username,
        taskId: e.task?.id,
        taskName: e.task?.name,
        duration: Math.round((parseInt(e.duration) / 3600000) * 10) / 10, // ms → hours
        start: e.start ? new Date(parseInt(e.start)).toISOString() : null,
        description: e.description || '',
      }));
    } catch (_) {} // time tracking may not be enabled on all plans

    // Aggregate time entries per member
    const timeByMember = {};
    timeEntries.forEach(e => {
      if (!timeByMember[e.userId]) timeByMember[e.userId] = { userId: e.userId, userName: e.userName, totalHours: 0, entries: [] };
      timeByMember[e.userId].totalHours += e.duration;
      timeByMember[e.userId].entries.push(e);
    });
    Object.values(timeByMember).forEach(m => {
      m.totalHours = Math.round(m.totalHours * 10) / 10;
    });

    const result = {
      members: Object.values(memberMap).filter(m => m.totalTasks > 0),
      allMembers: members,
      projects: Object.values(projectMap).sort((a, b) => b.total - a.total),
      timeEntries: timeEntries.slice(0, 100),
      timeByMember: Object.values(timeByMember).sort((a, b) => b.totalHours - a.totalHours),
      totals: {
        tasks: allTasks.length,
        done: allTasks.filter(t => t.status === 'done').length,
        inProgress: allTasks.filter(t => t.status === 'in_progress').length,
        overdue: allTasks.filter(t => t.dueDate && t.status !== 'done' && new Date(t.dueDate) < new Date()).length,
        timeSpentHours: Math.round(allTasks.reduce((s, t) => s + (t.timeSpent || 0), 0) * 10) / 10,
      },
    };

    cache.set(cacheKey, result, 120); // 2 min cache for team data
    res.json(result);
  } catch (err) { next(err); }
});

// ── POST /api/clickup/tasks/:id/status ───────────────────────
router.post('/tasks/:id/status', async (req, res, next) => {
  const { id } = req.params;
  const { status } = req.body;
  if (!status) return res.status(400).json({ error: 'status required' });
  try {
    await CU.put(`/task/${id}`, { status });
    cache.flushAll();
    res.json({ success: true });
  } catch (err) { next(err); }
});

module.exports = router;
