/**
 * Library Routes — central icon-code repository.
 *
 * Icons are stored as ICON CODE (raw SVG markup or an icon-library class/name),
 * not as uploaded image files. READ (list/search/get) is available to any
 * authenticated user, so icons can be SELECTED anywhere in the app. WRITE
 * (create/edit/delete) is restricted to Super Admins only.
 */

import express, { Request, Response } from 'express';
import crypto from 'crypto';
import mongoose from 'mongoose';
import { authenticate, requireRole } from '../middleware/auth';
import { getModels } from '../models';
import { LIBRARY_ICON_TYPES, type LibraryIconType } from '../models/LibraryIcon';

const router = express.Router();

// All library routes require authentication.
router.use(authenticate);

// ============================================
// HELPERS
// ============================================

function normaliseTags(input: unknown): string[] {
  if (Array.isArray(input)) {
    return input.map((t) => String(t).trim()).filter(Boolean);
  }
  if (typeof input === 'string') {
    // Accept comma-separated or JSON-encoded arrays.
    const trimmed = input.trim();
    if (!trimmed) return [];
    try {
      const parsed = JSON.parse(trimmed);
      if (Array.isArray(parsed)) return parsed.map((t) => String(t).trim()).filter(Boolean);
    } catch {
      /* not JSON — fall through to CSV parsing */
    }
    return trimmed.split(',').map((t) => t.trim()).filter(Boolean);
  }
  return [];
}

function isValidIconType(value: unknown): value is LibraryIconType {
  return typeof value === 'string' && (LIBRARY_ICON_TYPES as string[]).includes(value);
}

/** Stable checksum of an icon so identical code cannot be stored twice. */
function iconChecksum(iconType: string, iconCode: string): string {
  return crypto.createHash('sha256').update(`${iconType}\n${iconCode}`).digest('hex');
}

// ============================================
// READ — any authenticated user
// ============================================

/**
 * GET /
 * List icons with optional search + pagination.
 * Available to every authenticated user so icons can be selected anywhere.
 */
router.get('/', async (req: Request, res: Response) => {
  try {
    const { LibraryIcon } = getModels();

    const search = (req.query.search as string || '').trim();
    const page = Math.max(1, parseInt(req.query.page as string, 10) || 1);
    const limit = Math.min(500, Math.max(1, parseInt(req.query.limit as string, 10) || 100));

    const filter: Record<string, any> = {};
    if (search) {
      const rx = new RegExp(search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i');
      // Search across name, tags and icon type.
      filter.$or = [{ name: rx }, { tags: rx }, { iconType: rx }];
    }

    const [icons, total] = await Promise.all([
      LibraryIcon.find(filter)
        .sort({ createdAt: -1 })
        .skip((page - 1) * limit)
        .limit(limit)
        .lean(),
      LibraryIcon.countDocuments(filter),
    ]);

    res.json({ icons, total, page, limit });
  } catch (error) {
    console.error('Error listing library icons:', error);
    res.status(500).json({ error: 'Failed to list library icons' });
  }
});

// ============================================
// WRITE — Super Admin only
// ============================================

/**
 * POST /
 * Create a new icon from icon code. Super Admin only. Validates required fields
 * and rejects duplicates (identical iconType + iconCode).
 */
router.post('/', requireRole('super-admin'), async (req: Request, res: Response) => {
  try {
    const { LibraryIcon } = getModels();

    const name = String(req.body.name ?? '').trim();
    const iconType = req.body.iconType;
    const iconCode = String(req.body.iconCode ?? '').trim();
    const tags = normaliseTags(req.body.tags);

    if (!name) {
      res.status(400).json({ error: 'Name is required.' });
      return;
    }
    if (!isValidIconType(iconType)) {
      res.status(400).json({ error: 'A valid icon type is required.' });
      return;
    }
    if (!iconCode) {
      res.status(400).json({ error: 'Icon code is required.' });
      return;
    }

    const checksum = iconChecksum(iconType, iconCode);

    const existing = await LibraryIcon.findOne({ checksum }).lean();
    if (existing) {
      res.status(409).json({
        error: 'This icon code already exists in the Library.',
        duplicateOf: { id: (existing as any)._id.toString(), name: (existing as any).name },
      });
      return;
    }

    const doc = new LibraryIcon({
      name,
      tags,
      iconType,
      iconCode,
      checksum,
      uploadedBy: req.user!._id.toString(),
      uploadedByEmail: req.user!.email,
    });
    await doc.save();

    res.status(201).json({ icon: doc.toObject() });
  } catch (error: any) {
    // Unique-index race → treat as duplicate.
    if (error?.code === 11000) {
      res.status(409).json({ error: 'This icon code already exists in the Library.' });
      return;
    }
    console.error('Error creating library icon:', error);
    res.status(500).json({ error: 'Failed to create icon' });
  }
});

/**
 * PUT /:id
 * Edit an icon (name/tags/iconType/iconCode). Super Admin only.
 * Recomputes the checksum when the code/type changes and rejects duplicates.
 */
router.put('/:id', requireRole('super-admin'), async (req: Request, res: Response) => {
  try {
    const { LibraryIcon } = getModels();
    const icon = await LibraryIcon.findById(req.params.id);
    if (!icon) {
      res.status(404).json({ error: 'Icon not found' });
      return;
    }

    if (req.body.name !== undefined) {
      const name = String(req.body.name).trim();
      if (!name) {
        res.status(400).json({ error: 'Name cannot be empty' });
        return;
      }
      icon.name = name;
    }
    if (req.body.tags !== undefined) icon.tags = normaliseTags(req.body.tags);

    // Determine the resulting type/code so we can validate + recompute checksum.
    let nextType: string = icon.iconType;
    let nextCode: string = icon.iconCode;

    if (req.body.iconType !== undefined) {
      if (!isValidIconType(req.body.iconType)) {
        res.status(400).json({ error: 'A valid icon type is required.' });
        return;
      }
      nextType = req.body.iconType;
    }
    if (req.body.iconCode !== undefined) {
      nextCode = String(req.body.iconCode).trim();
      if (!nextCode) {
        res.status(400).json({ error: 'Icon code cannot be empty' });
        return;
      }
    }

    if (nextType !== icon.iconType || nextCode !== icon.iconCode) {
      const checksum = iconChecksum(nextType, nextCode);
      const clash = await LibraryIcon.findOne({ checksum, _id: { $ne: icon._id } }).lean();
      if (clash) {
        res.status(409).json({ error: 'This icon code already exists in the Library.' });
        return;
      }
      icon.iconType = nextType as LibraryIconType;
      icon.iconCode = nextCode;
      icon.checksum = checksum;
    }

    await icon.save();
    res.json({ icon: icon.toObject() });
  } catch (error: any) {
    if (error?.code === 11000) {
      res.status(409).json({ error: 'This icon code already exists in the Library.' });
      return;
    }
    console.error('Error updating library icon:', error);
    res.status(500).json({ error: 'Failed to update icon' });
  }
});

/**
 * DELETE /:id
 * Delete an icon. Super Admin only.
 *
 * Validates the id up front so a malformed value returns a clear 400 instead of
 * throwing a Mongoose CastError that surfaces as a generic 500. On unexpected
 * failure the real error message is logged and returned for diagnosis.
 */
router.delete('/:id', requireRole('super-admin'), async (req: Request, res: Response) => {
  const { id } = req.params;

  if (!id || !mongoose.isValidObjectId(id)) {
    res.status(400).json({ error: `Invalid icon id: "${id}".` });
    return;
  }

  try {
    const { LibraryIcon } = getModels();
    const deleted = await LibraryIcon.findByIdAndDelete(id);
    if (!deleted) {
      res.status(404).json({ error: 'Icon not found' });
      return;
    }

    res.json({ success: true, message: 'Icon deleted successfully', id });
  } catch (error: any) {
    console.error('Error deleting library icon:', error);
    res.status(500).json({ error: error?.message || 'Failed to delete icon' });
  }
});

export default router;
