import { Router } from 'express';
import Joi from 'joi';

import dayjs from '../libs/dayjs';
import { authenticate } from '../libs/security';
import logger from '../libs/logger';
import { createFlexibleEvent } from '../libs/audit';
import { getRetentionConfig } from '../libs/archive/config';
import { previewArchive } from '../libs/archive/executor';
import { queryArchive } from '../libs/archive/query';
import { enqueueArchiveJob } from '../queues/archive';
import { ArchiveMetadata } from '../store/models/archive-metadata';
import { listArchiveFiles, getFileSize } from '../libs/archive/store';

const router = Router();
const authAdmin = authenticate({ component: true, roles: ['owner', 'admin'] });

const querySchema = Joi.object({
  id: Joi.string().optional(),
  customer_id: Joi.string().optional(),
  from: Joi.number().integer().required(),
  to: Joi.number().integer().optional(),
  page: Joi.number().integer().min(1).default(1),
  limit: Joi.number().integer().min(1).max(100).default(20),
});

router.get('/status', authAdmin, async (_req, res) => {
  try {
    const config = getRetentionConfig();
    const lastRun = await ArchiveMetadata.findOne({ order: [['created_at', 'DESC']] });
    const oldest = await ArchiveMetadata.findOne({ order: [['created_at', 'ASC']] });
    const archives = await listArchiveFiles();
    const totalSize = archives.reduce((sum, file) => sum + getFileSize(file), 0);

    // Calculate next Wednesday at scheduled hour (cron: 0 0 ${hour} * * 3)
    const scheduleHour = config.schedule.hour;
    let nextRun = dayjs().day(3).hour(scheduleHour).minute(0).second(0).millisecond(0);
    if (nextRun.isBefore(dayjs())) {
      nextRun = nextRun.add(1, 'week');
    }

    res.json({
      enabled: config.enabled,
      config,
      lastRun: lastRun
        ? {
            id: lastRun.id,
            timestamp: lastRun.created_at,
            duration_ms: lastRun.duration_ms,
            status: lastRun.status,
            tables_processed: Object.keys(lastRun.tables || {}).length,
            total_archived: lastRun.total_records,
            total_failed: Object.values(lastRun.tables || {}).reduce(
              (sum: number, x: any) => sum + (x.failed_count || 0),
              0
            ),
          }
        : undefined,
      nextScheduledRun: config.schedule.enabled ? nextRun.unix() : undefined,
      archives: {
        count: archives.length,
        totalSize,
        oldestArchive: oldest?.archive_file,
        newestArchive: lastRun?.archive_file,
      },
    });
  } catch (error: any) {
    logger.error('archive status error', error);
    res.status(500).json({ error: error.message });
  }
});

router.get('/:table', authAdmin, async (req, res) => {
  try {
    const config = getRetentionConfig();
    if (!config.enabled) {
      return res.status(400).json({ error: 'Archive is disabled' });
    }

    const { value, error } = querySchema.validate(req.query, { stripUnknown: true, convert: true });
    if (error) {
      return res.status(400).json({ error: error.message });
    }

    const { table } = req.params;
    if (!table) {
      return res.status(400).json({ error: 'table is required' });
    }
    if (!Object.prototype.hasOwnProperty.call(config.tables, table)) {
      return res.status(400).json({ error: `Unsupported table: ${table}` });
    }

    if (value.from === undefined) {
      return res.status(400).json({ error: 'from is required' });
    }

    const result = await queryArchive(
      {
        table,
        id: value.id,
        customer_id: value.customer_id,
        from: value.from,
        to: value.to,
        page: value.page,
        limit: value.limit,
      },
      req.user?.did
    );

    await createFlexibleEvent(
      'archive.query',
      'Archive',
      table,
      {
        table,
        time_range: { from: value.from, to: value.to },
        result_count: result.data.length,
        archive_files: result.archiveFiles,
      },
      { requestedBy: req.user?.did }
    );

    return res.json({
      data: result.data,
      pagination: {
        page: value.page,
        limit: value.limit,
        total: result.total,
        hasMore: value.page * value.limit < result.total,
      },
      source: {
        archive_files: result.archiveFiles,
      },
    });
  } catch (error: any) {
    logger.error('archive query error', error);
    return res.status(500).json({ error: error.message });
  }
});

const runSchema = Joi.object({
  tables: Joi.array().items(Joi.string()).optional(),
  dryRun: Joi.boolean().optional(),
});

router.post('/run', authAdmin, async (req, res) => {
  try {
    const config = getRetentionConfig();
    if (!config.enabled) {
      return res.status(400).json({ error: 'Archive is disabled' });
    }

    const { value, error } = runSchema.validate(req.body, { stripUnknown: true });
    if (error) {
      return res.status(400).json({ error: error.message });
    }

    if (value?.dryRun) {
      const preview = await previewArchive({ tables: value.tables });
      return res.json({ status: 'dry_run_complete', preview });
    }

    const jobId = enqueueArchiveJob({
      tables: value.tables,
      triggeredBy: 'manual',
      triggeredByUserId: req.user?.did,
    });

    return res.json({ status: 'started', jobId });
  } catch (error: any) {
    logger.error('archive run error', error);
    return res.status(500).json({ error: error.message });
  }
});

export default router;
