// Background scheduler — runs inside the server process.
//   • Weekly (Mon) or monthly (1st) client reports, emailed if SMTP is set.
//   • Daily anomaly check across all sites, emailed + surfaced in the UI.
//
// .env:
//   REPORT_SCHEDULE=weekly     # weekly | monthly | off
//   REPORT_HOUR=7              # local hour to run
//   REPORT_EMAIL_TO=you@agency.co.uk   # comma-separated; empty = save only
//   ALERTS=1                   # 0 to disable anomaly checks
//   ALERT_EMAIL_TO=you@agency.co.uk    # falls back to REPORT_EMAIL_TO

import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { loadSites, timeseries, breakdown, resolveRange } from './ga4.js';
import { generateReport, pptxReport } from './report.js';
import { sendMail, mailConfigured } from './mailer.js';
import { listSchedules, markScheduleRun } from './repstore.js';
import { logActivity } from './audit.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const DATA_DIR = path.join(__dirname, '..', 'data');
const STATE_FILE = path.join(DATA_DIR, 'scheduler.json');
const ALERTS_FILE = path.join(DATA_DIR, 'alerts.json');

function loadJson(file, fallback) {
  try { return JSON.parse(fs.readFileSync(file, 'utf8')); } catch { return fallback; }
}
function saveJson(file, obj) {
  fs.mkdirSync(DATA_DIR, { recursive: true });
  fs.writeFileSync(file, JSON.stringify(obj, null, 2));
}

export function recentAlerts(days = 7) {
  const cutoff = Date.now() - days * 86400000;
  return loadJson(ALERTS_FILE, []).filter((a) => a.at > cutoff);
}

const fmtInt = (n) => Math.round(n).toLocaleString('en-GB');
const median = (arr) => {
  const s = [...arr].sort((a, b) => a - b);
  return s.length ? s[Math.floor(s.length / 2)] : 0;
};

async function anomalyCheck() {
  const alerts = [];
  const range = resolveRange('28d'); // ends yesterday
  for (const site of loadSites()) {
    try {
      const ts = await timeseries(site.id, range.startDate, range.endDate);
      if (ts.length < 8) continue;
      const yesterday = ts[ts.length - 1];
      const baseline = median(ts.slice(0, -1).map((r) => r.sessions));
      if (baseline < 20) continue;
      const ratio = yesterday.sessions / baseline;
      if (ratio >= 2.2 || ratio <= 0.4) {
        const day = yesterday.date;
        let causePage = null, causeSource = null;
        try {
          [causePage] = await breakdown(site.id, 'pagePath', day, day, 1);
          [causeSource] = await breakdown(site.id, 'sessionSource', day, day, 1);
        } catch { /* cause is optional */ }
        alerts.push({
          at: Date.now(),
          siteId: site.id,
          siteName: site.name,
          date: day,
          kind: ratio >= 2.2 ? 'spike' : 'dip',
          sessions: yesterday.sessions,
          typical: baseline,
          ratio: Number(ratio.toFixed(2)),
          topPage: causePage?.dimensionValue || null,
          topSource: causeSource?.dimensionValue || null,
        });
      }
    } catch (err) {
      console.error(`[scheduler] anomaly check failed for ${site.id}:`, err.message);
    }
  }
  if (alerts.length) {
    const existing = loadJson(ALERTS_FILE, []);
    saveJson(ALERTS_FILE, [...existing, ...alerts].slice(-200));
    const to = process.env.ALERT_EMAIL_TO || process.env.REPORT_EMAIL_TO;
    if (to && mailConfigured()) {
      const rows = alerts.map((a) =>
        `<li><b>${a.siteName}</b> — ${a.kind === 'spike' ? '📈 spike' : '📉 dip'} on ${a.date}: ${fmtInt(a.sessions)} sessions (${a.ratio}× the typical ${fmtInt(a.typical)})${a.topPage ? `, top page <code>${a.topPage}</code>` : ''}${a.topSource ? ` via ${a.topSource}` : ''}</li>`
      ).join('');
      try {
        await sendMail({
          to,
          subject: `LAV Pulse alert: ${alerts.map((a) => a.siteName).join(', ')}`,
          html: `<p>Traffic anomalies detected yesterday:</p><ul>${rows}</ul><p>Open the dashboard for the full picture.</p>`,
        });
      } catch (err) { console.error('[scheduler] alert email failed:', err.message); }
    }
  }
  return alerts.length;
}

async function runReports(kind) {
  const rangeStr = kind === 'monthly' ? '28d' : '7d';
  const to = process.env.REPORT_EMAIL_TO;
  const generated = [];
  const scopes = ['all', ...loadSites().map((s) => s.id)];
  for (const scope of scopes) {
    try {
      const { filename, html, title } = await generateReport(scope, rangeStr);
      generated.push(filename);
      if (to && mailConfigured()) {
        await sendMail({ to, subject: `${title} (${kind})`, html });
      }
    } catch (err) {
      console.error(`[scheduler] report failed for ${scope}:`, err.message);
    }
  }
  console.log(`[scheduler] generated ${generated.length} ${kind} report(s)`);
  return generated;
}

// Per-client report schedules created in the Reports section — each one
// generates its report (PDF report or PowerPoint deck) and emails it when
// recipients are set and SMTP is configured.
async function runCustomSchedules(now, today) {
  for (const sch of listSchedules()) {
    try {
      if (!sch.enabled || sch.lastRun === today || now.getHours() < (sch.hour ?? 7)) continue;
      const due = sch.frequency === 'daily'
        || (sch.frequency === 'weekly' && now.getDay() === (sch.day ?? 1) % 7)
        || (sch.frequency === 'monthly' && now.getDate() === Math.max(1, sch.day ?? 1));
      if (!due) continue;
      markScheduleRun(sch.id, null); // claim first so a crash can't loop it
      const site = loadSites().find((s) => s.id === sch.siteId);
      let file = null, title = '';
      if (sch.format === 'pptx' && sch.siteId !== 'all') {
        const rp = await pptxReport({
          siteId: sch.siteId, range: sch.range,
          title: `${site?.name || sch.siteId} — scheduled ${sch.style === 'monitoring' ? 'monitoring ' : ''}report`,
          subtitle: `${sch.frequency} · last ${sch.range}`,
          monitoring: sch.style === 'monitoring', market: sch.market || null, source: 'schedule',
        });
        file = rp.filename; title = `${site?.name || sch.siteId} — scheduled deck`;
      } else {
        const rep = await generateReport(sch.siteId, sch.range, sch.style, { market: sch.market || null, source: 'schedule' });
        file = rep.pdfFilename || rep.filename; title = rep.title;
        if (sch.emails && mailConfigured()) {
          await sendMail({ to: sch.emails, subject: `${rep.title} (${sch.frequency} report)`, html: rep.html })
            .catch((e) => console.error('[scheduler] schedule email failed:', e.message));
        }
      }
      markScheduleRun(sch.id, file);
      logActivity('scheduler', 'scheduled-report', { schedule: sch.id, siteId: sch.siteId, style: sch.style, format: sch.format, file });
      console.log(`[scheduler] schedule ${sch.id} (${sch.siteId}/${sch.style}/${sch.format}) → ${file}`);
    } catch (err) {
      console.error(`[scheduler] schedule ${sch.id} failed:`, err.message);
    }
  }
}

async function tick() {
  const state = loadJson(STATE_FILE, {});
  const now = new Date();
  const today = now.toISOString().slice(0, 10);
  const hour = Number(process.env.REPORT_HOUR || 7);
  await runCustomSchedules(now, today).catch((e) => console.error(e));

  // Daily anomaly check (once per day, after the report hour so yesterday is complete)
  if (process.env.ALERTS !== '0' && state.lastAnomalyDate !== today && now.getHours() >= hour) {
    state.lastAnomalyDate = today;
    saveJson(STATE_FILE, state);
    const n = await anomalyCheck().catch((e) => { console.error(e); return 0; });
    if (n) console.log(`[scheduler] ${n} anomaly alert(s) raised`);
  }

  // Scheduled reports
  const schedule = (process.env.REPORT_SCHEDULE || 'off').toLowerCase();
  const due =
    (schedule === 'weekly' && now.getDay() === 1) ||
    (schedule === 'monthly' && now.getDate() === 1);
  if (schedule !== 'off' && due && now.getHours() >= hour && state.lastReportDate !== today) {
    state.lastReportDate = today;
    saveJson(STATE_FILE, state);
    await runReports(schedule).catch((e) => console.error(e));
  }
}

export function startScheduler() {
  // Daily media-counter snapshots — powers dated per-asset download answers.
  import('./snapshots.js').then(({ captureAllMediaSnapshots }) => {
    setTimeout(() => captureAllMediaSnapshots().catch(() => {}), 45 * 1000);
    setInterval(() => captureAllMediaSnapshots().catch(() => {}), 30 * 60 * 1000); // per-day guard inside
  }).catch(() => {});
  setTimeout(() => tick().catch((e) => console.error(e)), 20 * 1000);
  setInterval(() => tick().catch((e) => console.error(e)), 30 * 60 * 1000);
  console.log(`Scheduler active: reports=${process.env.REPORT_SCHEDULE || 'off'}, alerts=${process.env.ALERTS !== '0' ? 'on' : 'off'}, email=${mailConfigured() ? 'configured' : 'not configured'}`);
}
