import { Request, Response, NextFunction } from 'express';
import { kpiQuerySchema } from './kpi.validation';
import { Lead } from '@/modules/lead/lead.model';
import { CaptureLead } from './captureLeads.model';
import { getObjectId } from '@/shared/utils/commonHelper';
import { FilterQuery, Types } from 'mongoose';

function defaultRangeUTC(from?: string, to?: string) {
  const now = new Date();
  const end = to
    ? new Date(to)
    : new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), 23, 59, 59, 999));
  const startBase = to ? new Date(to) : now;
  const startRaw = from
    ? new Date(from)
    : new Date(Date.UTC(startBase.getUTCFullYear(), startBase.getUTCMonth(), startBase.getUTCDate() - 29, 0, 0, 0, 0));
  const start = new Date(Date.UTC(startRaw.getUTCFullYear(), startRaw.getUTCMonth(), startRaw.getUTCDate(), 0, 0, 0, 0));
  return { start, end };
}

export async function getCaptureLeadsKpi(req: Request, res: Response, next: NextFunction) {
  try {
    const { value, error } = kpiQuerySchema.validate(req.query);
    if (error) return res.status(400).json({ success: false, code: 2001, message: error.message });

    const { from, to, search, platform, project } = value as {
      from?: string; to?: string; search?: string; platform?: string; project?: string;
    };

    const userCompany = (req as unknown as { user?: { company?: { id?: string } | string } }).user?.company;
    const companyId = typeof userCompany === 'string' ? userCompany : userCompany?.id;
    const { start, end } = defaultRangeUTC(from, to);

    // Filter capture leads by company and optional filters
    const clFilter: FilterQuery<unknown> = { company: getObjectId(companyId), isDeleted: { $ne: true } };
    if (search) clFilter.name = { $regex: search, $options: 'i' };
    if (platform) clFilter.platform = platform;
    if (project) clFilter.project = getObjectId(project);

    const captureLeads = await CaptureLead.find(clFilter)
      .select('_id platform')
      .lean<Array<{ _id: Types.ObjectId; platform?: string }>>();
    const clIds = captureLeads.map((c) => c._id);

    // totalIntegrations
    const totalIntegrations = captureLeads.length;

    // platformsConnected (distinct platforms across filtered active integrations)
    const platformsConnected = Array.from(new Set(captureLeads.map((c) => c.platform).filter(Boolean))).length;

    // Leads filter scoped to selected captureLeads (if any), company and period
    const leadMatch: Record<string, unknown> = {
      company: getObjectId(companyId),
      createdAt: { $gte: start, $lte: end },
    };
    if (clIds.length) leadMatch.captureLead = { $in: clIds };

    // totalLeads in range
    const [totalAgg] = await Lead.aggregate([
      { $match: leadMatch },
      { $count: 'totalLeads' },
    ]);
    const totalLeads = totalAgg?.totalLeads ?? 0;

    // Build daily counts
    const daily = await Lead.aggregate<{ day: string; y: number }>([
      { $match: leadMatch },
      {
        $group: {
          _id: { day: { $dateToString: { format: '%Y-%m-%d', date: '$createdAt' } } },
          y: { $sum: 1 },
        },
      },
      { $project: { day: '$_id.day', y: 1, _id: 0 } },
      { $sort: { day: 1 } },
    ]);

    // Compress into 12 buckets sparkline
    const buckets = 12;
    const spanMs = end.getTime() - start.getTime();
    const step = Math.max(Math.floor(spanMs / buckets), 1);
    const edges: Date[] = Array.from({ length: buckets }, (_, i) => new Date(start.getTime() + i * step));
    edges.push(end);

    const dayToCount = new Map<string, number>(daily.map((d) => [d.day, d.y]));
    const trend: number[] = [];
    for (let i = 0; i < buckets; i++) {
      const a = edges[i];
      const b = edges[i + 1];
      let sum = 0;
      for (let t = new Date(Date.UTC(a.getUTCFullYear(), a.getUTCMonth(), a.getUTCDate())); t <= b; t = new Date(t.getTime() + 24 * 3600 * 1000)) {
        const ymd = t.toISOString().slice(0, 10);
        sum += dayToCount.get(ymd) ?? 0;
      }
      trend.push(sum);
    }

    // Daily trend formatted for UI: [{ day: 'YYYY-MM-DD', leads: number }]
    const dailyTrend: Array<{ day: string; leads: number }> = [];
    for (
      let t = new Date(Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate()));
      t <= end;
      t = new Date(t.getTime() + 24 * 3600 * 1000)
    ) {
      const ymd = t.toISOString().slice(0, 10);
      dailyTrend.push({ day: ymd, leads: dayToCount.get(ymd) ?? 0 });
    }

    // Platform timeseries (daily)
    const platformRows = await Lead.aggregate([
      { $match: leadMatch },
      { $lookup: { from: 'captureleads', localField: 'captureLead', foreignField: '_id', as: 'cl' } },
      { $unwind: '$cl' },
      { $project: { platform: '$cl.platform', x: { $dateToString: { format: '%Y-%m-%d', date: '$createdAt' } } } },
      { $group: { _id: { platform: '$platform', x: '$x' }, y: { $sum: 1 } } },
      { $project: { platform: '$_id.platform', x: '$_id.x', y: 1, _id: 0 } },
      { $sort: { platform: 1, x: 1 } },
    ]);

    const byPlatform = new Map<string, Array<{ x: string; y: number }>>();
    for (const r of platformRows as Array<{ platform: string; x: string; y: number }>) {
      if (!byPlatform.has(r.platform)) byPlatform.set(r.platform, []);
      byPlatform.get(r.platform)!.push({ x: r.x, y: r.y });
    }
    const platformTimeseries = {
      granularity: 'day' as const,
      series: Array.from(byPlatform.entries()).map(([platform, points]) => ({ platform, points })),
    };

    return res.success(
      {
        overview: { totalIntegrations, totalLeads, period: { from: start.toISOString(), to: end.toISOString() } },
        platformsConnected,
        trend,
        platformTimeseries,
        dailyTrend,
      },
      200,
      'Capture Leads KPI Fetched',
    );
  } catch (err) {
    next(err);
  }
}
