import { NextRequest, NextResponse } from 'next/server';
import { getMsgraphClient } from '@/lib/services/msgraph-factory';
import { postgresClient } from '@/lib/services/postgres-client';

let backfillInProgress = false;
let backfillStatus: {
  running: boolean;
  started: string | null;
  processed: number;
  total: number;
  currentUser: string | null;
  errors: number;
  done: boolean;
  log: string[];
} = { running: false, started: null, processed: 0, total: 0, currentUser: null, errors: 0, done: false, log: [] };

export async function GET() {
  return NextResponse.json(backfillStatus);
}

export async function POST(request: NextRequest) {
  if (backfillInProgress) {
    return NextResponse.json({ error: 'Backfill already running' }, { status: 409 });
  }

  const body = await request.json().catch(() => ({}));
  const monthsBack = Math.min(Number(body.monthsBack ?? 12), 24);

  backfillInProgress = true;
  backfillStatus = {
    running: true,
    started: new Date().toISOString(),
    processed: 0,
    total: 0,
    currentUser: null,
    errors: 0,
    done: false,
    log: [],
  };

  // Run async — don't await
  runBackfill(monthsBack).finally(() => {
    backfillInProgress = false;
  });

  return NextResponse.json({ started: true, monthsBack });
}

async function runBackfill(monthsBack: number) {
  const log = (msg: string) => {
    console.log(`[MEETING-BACKFILL] ${msg}`);
    backfillStatus.log.push(msg);
    if (backfillStatus.log.length > 200) backfillStatus.log.shift();
  };

  try {
    const client = getMsgraphClient();

    // Fetch internal domains for attendee classification
    const orgDomains = await client.getOrganizationDomains();
    const internalDomains = new Set(orgDomains);
    log(`Internal domains: ${[...internalDomains].join(', ')}`);

    // Build contact email index for client matching
    const contactRows = await postgresClient.query(
      `SELECT id, company_id, LOWER(email_address) as e1,
              LOWER(email_address2) as e2, LOWER(email_address3) as e3
       FROM contacts WHERE (is_deleted = false OR is_deleted IS NULL)`
    );
    const contactEmailIndex = new Map<string, { contactId: number; companyId: number | null }>();
    for (const row of contactRows.rows) {
      for (const e of [row.e1, row.e2, row.e3]) {
        if (e && !contactEmailIndex.has(e)) {
          contactEmailIndex.set(e, { contactId: row.id, companyId: row.company_id });
        }
      }
    }
    log(`Contact index: ${contactEmailIndex.size} emails`);

    // Get all active graph users
    const usersResult = await postgresClient.query(
      `SELECT id, email, display_name FROM graph_users WHERE account_enabled = true ORDER BY display_name`
    );
    const users = usersResult.rows;
    backfillStatus.total = users.length;
    log(`Users to backfill: ${users.length}, going back ${monthsBack} months`);

    const now = new Date();
    // Build date range: from (monthsBack months ago, start of month) to 91 days ago
    // (avoid re-syncing data already covered by the regular 90-day sync)
    const backfillEnd = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);
    const backfillStart = new Date(now.getFullYear(), now.getMonth() - monthsBack, 1);
    log(`Date range: ${backfillStart.toISOString().slice(0, 10)} → ${backfillEnd.toISOString().slice(0, 10)}`);

    for (const user of users) {
      backfillStatus.currentUser = user.display_name;
      try {
        const events = await client.getUserCalendarEvents(user.id, backfillStart, backfillEnd);
        let inserted = 0;

        for (const event of events) {
          if (!event.id) continue;
          try {
            const startTime = new Date(event.start.dateTime);
            const endTime = new Date(event.end.dateTime);
            const durationMinutes = Math.max(0, Math.round((endTime.getTime() - startTime.getTime()) / 60000));
            const attendeeCount = event.attendees.length;

            const externalAttendees = event.attendees.filter(a => {
              const aEmail = (a.emailAddress?.address ?? '').toLowerCase();
              if (aEmail === user.email.toLowerCase()) return false;
              const domain = aEmail.split('@')[1];
              return domain && !internalDomains.has(domain);
            });

            const meetingResult = await postgresClient.query(
              `INSERT INTO teams_meetings
                 (graph_event_id, user_email, subject, start_time, end_time,
                  duration_minutes, is_online_meeting, attendee_count, synced_at)
               VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW())
               ON CONFLICT (user_email, graph_event_id) DO UPDATE SET
                 subject = EXCLUDED.subject,
                 start_time = EXCLUDED.start_time,
                 end_time = EXCLUDED.end_time,
                 duration_minutes = EXCLUDED.duration_minutes,
                 is_online_meeting = EXCLUDED.is_online_meeting,
                 attendee_count = EXCLUDED.attendee_count,
                 synced_at = NOW()
               RETURNING id`,
              [event.id, user.email, event.subject, startTime, endTime,
               durationMinutes, event.isOnlineMeeting, attendeeCount]
            );
            const meetingId = meetingResult.rows[0]?.id;
            if (!meetingId) continue;

            await postgresClient.query(
              `DELETE FROM teams_meeting_attendees WHERE meeting_id = $1`,
              [meetingId]
            );

            let clientCount = 0;
            for (const att of externalAttendees) {
              const attEmail = (att.emailAddress?.address ?? '').toLowerCase();
              const attName = att.emailAddress?.name ?? null;
              const match = attEmail ? contactEmailIndex.get(attEmail) : undefined;
              await postgresClient.query(
                `INSERT INTO teams_meeting_attendees
                   (meeting_id, attendee_email, attendee_name, matched_contact_id, matched_company_id)
                 VALUES ($1, $2, $3, $4, $5)`,
                [meetingId, attEmail || null, attName,
                 match?.contactId ?? null, match?.companyId ?? null]
              );
              if (match?.companyId) clientCount++;
            }

            await postgresClient.query(
              `UPDATE teams_meetings SET client_attendee_count = $1, has_client_attendees = $2 WHERE id = $3`,
              [clientCount, clientCount > 0, meetingId]
            );
            inserted++;
          } catch (evErr) {
            const msg = evErr instanceof Error ? evErr.message : String(evErr);
            log(`  Event error ${event.id}: ${msg}`);
          }
        }

        log(`${user.display_name}: ${events.length} events, ${inserted} upserted`);
      } catch (userErr) {
        const msg = userErr instanceof Error ? userErr.message : String(userErr);
        log(`${user.display_name}: SKIP — ${msg}`);
        backfillStatus.errors++;
      }

      backfillStatus.processed++;
    }

    log(`Done. ${backfillStatus.processed} users, ${backfillStatus.errors} errors.`);
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    log(`FATAL: ${msg}`);
  } finally {
    backfillStatus.running = false;
    backfillStatus.done = true;
    backfillStatus.currentUser = null;
  }
}
