import { Request, Response } from 'express';
import { Holiday, HolidayType } from '@models/Holiday';
import { holidayService } from '@services/holiday.service';
import { activityTypeService } from '@services/activityType.service';
import { sequelize } from '@config/database';
import { QueryTypes, Op } from 'sequelize';
import logger from '@utils/logger';
import { initializeHolidaysCache, clearWorkingHoursCache } from '@utils/tatTimeUtils';
import { clearConfigCache } from '@services/configReader.service';
import { User, UserRole } from '@models/User';
import { sanitizeHtml, sanitizeObject, isHtmlEmpty } from '@utils/sanitizer';
import {
  CPC_CSD_ADMIN_CONFIG_KEY,
  CPC_CDC_ADMIN_CONFIG_KEY_LEGACY,
  selectCpcCsdAdminConfigValue,
} from '@utils/cpcCsdAdminConfigDb';

/**
 * Get all holidays (with optional year filter)
 */
export const getAllHolidays = async (req: Request, res: Response): Promise<void> => {
  try {
    const { year } = req.query;
    const yearNum = year ? parseInt(year as string) : undefined;

    const holidays = await holidayService.getAllActiveHolidays(yearNum);

    res.json({
      success: true,
      data: holidays,
      count: holidays.length
    });
  } catch (error) {
    logger.error('[Admin] Error fetching holidays:', error);
    res.status(500).json({
      success: false,
      error: 'Failed to fetch holidays'
    });
  }
};

/**
 * Get holiday calendar for a specific year
 */
export const getHolidayCalendar = async (req: Request, res: Response): Promise<void> => {
  try {
    const { year } = req.params;
    const yearNum = parseInt(year);

    if (isNaN(yearNum) || yearNum < 2000 || yearNum > 2100) {
      res.status(400).json({
        success: false,
        error: 'Invalid year'
      });
      return;
    }

    const calendar = await holidayService.getHolidayCalendar(yearNum);

    res.json({
      success: true,
      year: yearNum,
      holidays: calendar,
      count: calendar.length
    });
  } catch (error) {
    logger.error('[Admin] Error fetching holiday calendar:', error);
    res.status(500).json({
      success: false,
      error: 'Failed to fetch holiday calendar'
    });
  }
};

/**
 * Create a new holiday
 */
export const createHoliday = async (req: Request, res: Response): Promise<void> => {
  try {
    const userId = req.user?.userId;
    if (!userId) {
      res.status(401).json({
        success: false,
        error: 'User not authenticated'
      });
      return;
    }

    const {
      holidayDate,
      holidayName,
      description,
      holidayType,
      isRecurring,
      recurrenceRule,
      appliesToDepartments,
      appliesToLocations
    } = req.body;

    // Validate required fields
    if (!holidayDate || !holidayName) {
      res.status(400).json({
        success: false,
        error: 'Holiday date and name are required'
      });
      return;
    }

    const holiday = await holidayService.createHoliday({
      holidayDate,
      holidayName,
      description: description ? sanitizeHtml(description) : description,
      holidayType: holidayType || HolidayType.ORGANIZATIONAL,
      isRecurring: isRecurring || false,
      recurrenceRule,
      appliesToDepartments,
      appliesToLocations,
      createdBy: userId
    });

    // Reload holidays cache
    await initializeHolidaysCache();

    res.status(201).json({
      success: true,
      message: 'Holiday created successfully',
      data: holiday
    });
  } catch (error: any) {
    logger.error('[Admin] Error creating holiday:', error);
    res.status(500).json({
      success: false,
      message: 'Failed to create holiday',
      error: error.message,
      details: error.errors // Sequelize validation errors are usually in .errors
    });
  }
};

/**
 * Update a holiday
 */
export const updateHoliday = async (req: Request, res: Response): Promise<void> => {
  try {
    const userId = req.user?.userId;
    if (!userId) {
      res.status(401).json({
        success: false,
        error: 'User not authenticated'
      });
      return;
    }

    const { holidayId } = req.params;
    const updates = req.body;
    if (updates.description) {
      updates.description = sanitizeHtml(updates.description);
    }

    const holiday = await holidayService.updateHoliday(holidayId, updates, userId);

    if (!holiday) {
      res.status(404).json({
        success: false,
        error: 'Holiday not found'
      });
      return;
    }

    // Reload holidays cache
    await initializeHolidaysCache();

    res.json({
      success: true,
      message: 'Holiday updated successfully',
      data: holiday
    });
  } catch (error: any) {
    logger.error('[Admin] Error updating holiday:', error);
    res.status(500).json({
      success: false,
      message: 'Failed to update holiday',
      error: error.message,
      details: error.errors
    });
  }
};

/**
 * Delete (deactivate) a holiday
 */
export const deleteHoliday = async (req: Request, res: Response): Promise<void> => {
  try {
    const { holidayId } = req.params;

    await holidayService.deleteHoliday(holidayId);

    // Reload holidays cache
    await initializeHolidaysCache();

    res.json({
      success: true,
      message: 'Holiday deleted successfully'
    });
  } catch (error: any) {
    logger.error('[Admin] Error deleting holiday:', error);
    res.status(500).json({
      success: false,
      error: error.message || 'Failed to delete holiday'
    });
  }
};

/**
 * Bulk import holidays from CSV/JSON
 */
export const bulkImportHolidays = async (req: Request, res: Response): Promise<void> => {
  try {
    const userId = req.user?.userId;
    if (!userId) {
      res.status(401).json({
        success: false,
        error: 'User not authenticated'
      });
      return;
    }

    const { holidays } = req.body;

    if (!Array.isArray(holidays) || holidays.length === 0) {
      res.status(400).json({
        success: false,
        error: 'Holidays array is required'
      });
      return;
    }

    const result = await holidayService.bulkImportHolidays(holidays, userId);

    // Reload holidays cache
    await initializeHolidaysCache();

    res.json({
      success: true,
      message: `Imported ${result.success} holidays, ${result.failed} failed`,
      data: result
    });
  } catch (error: any) {
    logger.error('[Admin] Error bulk importing holidays:', error);
    res.status(500).json({
      success: false,
      error: error.message || 'Failed to import holidays'
    });
  }
};

/**
 * Get public configurations (read-only, non-sensitive)
 * Accessible to all authenticated users
 */
export const getPublicConfigurations = async (req: Request, res: Response): Promise<void> => {
  try {
    const { category } = req.query;

    // Only allow certain categories for public access
    const allowedCategories = ['DOCUMENT_POLICY', 'TAT_SETTINGS', 'WORKFLOW_SHARING', 'SYSTEM_SETTINGS'];
    if (category && !allowedCategories.includes(category as string)) {
      res.status(403).json({
        success: false,
        error: 'Access denied to this configuration category'
      });
      return;
    }

    let whereClause = '';
    if (category) {
      whereClause = `WHERE config_category = '${category}' AND is_sensitive = false`;
    } else {
      whereClause = `WHERE config_category IN ('DOCUMENT_POLICY', 'TAT_SETTINGS', 'WORKFLOW_SHARING', 'SYSTEM_SETTINGS') AND is_sensitive = false`;
    }

    const rawConfigurations = await sequelize.query(`
      SELECT 
        config_key,
        config_category,
        config_value,
        value_type,
        display_name,
        description
      FROM admin_configurations
      ${whereClause}
      ORDER BY config_category, sort_order
    `, { type: QueryTypes.SELECT });

    // Map snake_case to camelCase for frontend
    const configurations = (rawConfigurations as any[]).map((config: any) => ({
      configKey: config.config_key,
      configCategory: config.config_category,
      configValue: config.config_value,
      valueType: config.value_type,
      displayName: config.display_name,
      description: config.description
    }));

    res.json({
      success: true,
      data: configurations,
      count: configurations.length
    });
  } catch (error) {
    logger.error('[Admin] Error fetching public configurations:', error);
    res.status(500).json({
      success: false,
      error: 'Failed to fetch configurations'
    });
  }
};

/**
 * Get all admin configurations
 */
export const getAllConfigurations = async (req: Request, res: Response): Promise<void> => {
  try {
    const { category } = req.query;

    let whereClause = '';
    if (category) {
      whereClause = `WHERE config_category = '${category}'`;
    }

    const rawConfigurations = await sequelize.query(`
      SELECT 
        config_id,
        config_key,
        config_category,
        config_value,
        value_type,
        display_name,
        description,
        default_value,
        is_editable,
        is_sensitive,
        validation_rules,
        ui_component,
        options,
        sort_order,
        requires_restart,
        last_modified_at,
        last_modified_by
      FROM admin_configurations
      ${whereClause}
      ORDER BY config_category, sort_order
    `, { type: QueryTypes.SELECT });

    // Map snake_case to camelCase for frontend
    const configurations = (rawConfigurations as any[]).map((config: any) => ({
      configId: config.config_id,
      configKey: config.config_key,
      configCategory: config.config_category,
      configValue: config.config_value,
      valueType: config.value_type,
      displayName: config.display_name,
      description: config.description,
      defaultValue: config.default_value,
      isEditable: config.is_editable,
      isSensitive: config.is_sensitive || false,
      validationRules: config.validation_rules,
      uiComponent: config.ui_component,
      options: config.options,
      sortOrder: config.sort_order,
      requiresRestart: config.requires_restart || false,
      lastModifiedAt: config.last_modified_at,
      lastModifiedBy: config.last_modified_by
    }));

    res.json({
      success: true,
      data: configurations,
      count: configurations.length
    });
  } catch (error) {
    logger.error('[Admin] Error fetching configurations:', error);
    res.status(500).json({
      success: false,
      error: 'Failed to fetch configurations'
    });
  }
};

/**
 * Update a configuration
 */
export const updateConfiguration = async (req: Request, res: Response): Promise<void> => {
  try {
    const userId = req.user?.userId;
    if (!userId) {
      res.status(401).json({
        success: false,
        error: 'User not authenticated'
      });
      return;
    }

    const { configKey } = req.params;
    let { configValue } = req.body;

    if (configValue === undefined) {
      res.status(400).json({
        success: false,
        error: 'Config value is required'
      });
      return;
    }

    // Sanitize config value using unified sanitizeObject
    // This will handle strings, numbers, and nested objects consistently
    const sanitizedObj = sanitizeObject({ [configKey]: configValue });
    configValue = sanitizedObj[configKey];

    // If it's a string, ensure it's not effectively empty after sanitization
    if (typeof configValue === 'string' && isHtmlEmpty(configValue)) {
      res.status(400).json({
        success: false,
        error: 'Config value is required and must contain valid content'
      });
      return;
    }

    // Update configuration
    const result = await sequelize.query(`
      UPDATE admin_configurations
      SET 
        config_value = :configValue,
        last_modified_by = :userId,
        last_modified_at = NOW(),
        updated_at = NOW()
      WHERE config_key = :configKey
        AND is_editable = true
      RETURNING *
    `, {
      replacements: { configValue, userId, configKey },
      type: QueryTypes.UPDATE
    });

    if (!result || (result[1] as any) === 0) {
      res.status(404).json({
        success: false,
        error: 'Configuration not found or not editable'
      });
      return;
    }

    // Clear config cache so new values are used immediately
    clearConfigCache();

    // If working hours config was updated, also clear working hours cache
    const workingHoursKeys = ['WORK_START_HOUR', 'WORK_END_HOUR', 'WORK_START_DAY', 'WORK_END_DAY'];
    if (workingHoursKeys.includes(configKey)) {
      await clearWorkingHoursCache();
      logger.info(`[Admin] Working hours configuration '${configKey}' updated - cache cleared and reloaded`);
    }

    // If AI config was updated, reinitialize AI service
    const aiConfigKeys = ['AI_ENABLED'];
    if (aiConfigKeys.includes(configKey)) {
      try {
        const { aiService } = require('../services/ai.service');
        await aiService.reinitialize();
        logger.info(`[Admin] AI configuration '${configKey}' updated - AI service reinitialized with ${aiService.getProviderName()}`);
      } catch (error) {
        logger.error(`[Admin] Failed to reinitialize AI service:`, error);
      }
    } else {
      logger.info(`[Admin] Configuration '${configKey}' updated and cache cleared`);
    }

    res.json({
      success: true,
      message: 'Configuration updated successfully'
    });
  } catch (error: any) {
    logger.error('[Admin] Error updating configuration:', error);
    res.status(500).json({
      success: false,
      error: error.message || 'Failed to update configuration'
    });
  }
};

/**
 * Reset configuration to default value
 */
export const resetConfiguration = async (req: Request, res: Response): Promise<void> => {
  try {
    const { configKey } = req.params;

    await sequelize.query(`
      UPDATE admin_configurations
      SET config_value = default_value,
          updated_at = NOW()
      WHERE config_key = :configKey
    `, {
      replacements: { configKey },
      type: QueryTypes.UPDATE
    });

    // Clear config cache so reset values are used immediately
    clearConfigCache();

    // If working hours config was reset, also clear working hours cache
    const workingHoursKeys = ['WORK_START_HOUR', 'WORK_END_HOUR', 'WORK_START_DAY', 'WORK_END_DAY'];
    if (workingHoursKeys.includes(configKey)) {
      await clearWorkingHoursCache();
      logger.info(`[Admin] Working hours configuration '${configKey}' reset to default - cache cleared and reloaded`);
    } else {
      logger.info(`[Admin] Configuration '${configKey}' reset to default and cache cleared`);
    }

    res.json({
      success: true,
      message: 'Configuration reset to default'
    });
  } catch (error: any) {
    logger.error('[Admin] Error resetting configuration:', error);
    res.status(500).json({
      success: false,
      error: error.message || 'Failed to reset configuration'
    });
  }
};

/** Form 16 admin config – stored in same admin_configurations table as other workflow/admin configs (config_key = FORM16_ADMIN_CONFIG, config_value = JSON). */
const FORM16_CONFIG_KEY = 'FORM16_ADMIN_CONFIG';

/** Normalize run-at time to HH:mm (e.g. "9:0" -> "09:00"). */
function normalizeRunAtTime(s: string): string {
  const [h, m] = s.split(':').map((x) => parseInt(x, 10));
  if (Number.isNaN(h) || Number.isNaN(m)) return '09:00';
  const hh = Math.max(0, Math.min(23, h));
  const mm = Math.max(0, Math.min(59, m));
  return `${String(hh).padStart(2, '0')}:${String(mm).padStart(2, '0')}`;
}

const defaultNotificationItem = (template: string) => ({ enabled: true, template });
/** 26AS data added: separate message for RE users and for dealers */
const default26AsNotification = () => ({
  enabled: true,
  templateRe: '26AS data has been added. Please review and use for matching dealer Form 16 submissions.',
  templateDealers: 'New 26AS data has been uploaded. You can now submit your Form 16 for the relevant quarter if you haven’t already.',
});
const DEFAULT_FORM16_CONFIG = {
  submissionViewerEmails: [] as string[],
  twentySixAsViewerEmails: [] as string[],
  reminderEnabled: true,
  reminderDays: 7,
  // Form 16 notification events (each: enabled + optional template(s))
  notification26AsDataAdded: default26AsNotification(),
  notificationForm16SuccessCreditNote: defaultNotificationItem('Form 16 submitted successfully. Credit note: [CreditNoteRef].'),
  notificationForm16Unsuccessful: defaultNotificationItem('Form 16 submission was unsuccessful. Issue: [Issue].'),
  alertSubmitForm16Enabled: true,
  alertSubmitForm16FrequencyDays: 0,
  alertSubmitForm16FrequencyHours: 24,
  alertSubmitForm16RunAtTime: '09:00',
  alertSubmitForm16Template: 'Dear [Name], please submit Form 16A for the pending period. Due: [DueDate].',
  reminderNotificationEnabled: true,
  reminderFrequencyDays: 0,
  reminderFrequencyHours: 12,
  reminderRunAtTime: '10:00',
  reminderNotificationTemplate: 'Reminder: Dear [Name], your Form 16A submission is pending for request [Request ID]. Please complete it.',
};

const DEFAULT_CPC_CSD_CONFIG = {
  viewerEmails: [] as string[],
};

/**
 * Get Form 16 admin configuration (who can see submission data, 26AS, reminders)
 */
export const getForm16Config = async (req: Request, res: Response): Promise<void> => {
  try {
    const result = await sequelize.query<{ config_value: string }>(
      `SELECT config_value FROM admin_configurations WHERE config_key = :configKey LIMIT 1`,
      { replacements: { configKey: FORM16_CONFIG_KEY }, type: QueryTypes.SELECT }
    );
    if (result && result.length > 0 && result[0].config_value) {
      try {
        const parsed = JSON.parse(result[0].config_value);
        const mergeNotification = (key: keyof typeof DEFAULT_FORM16_CONFIG) => {
          const val = parsed[key];
          const def = DEFAULT_FORM16_CONFIG[key] as any;
          if (val && typeof val === 'object' && typeof val.enabled === 'boolean') {
            return { enabled: val.enabled, template: typeof val.template === 'string' ? val.template : (def?.template ?? '') };
          }
          return def;
        };
        const def26As = DEFAULT_FORM16_CONFIG.notification26AsDataAdded as { enabled: boolean; templateRe: string; templateDealers: string };
        const merge26As = () => {
          const val = parsed.notification26AsDataAdded;
          if (val && typeof val === 'object' && typeof val.enabled === 'boolean') {
            return {
              enabled: val.enabled,
              templateRe: typeof val.templateRe === 'string' ? val.templateRe : (typeof val.template === 'string' ? val.template : def26As.templateRe),
              templateDealers: typeof val.templateDealers === 'string' ? val.templateDealers : def26As.templateDealers,
            };
          }
          return def26As;
        };
        res.json({
          success: true,
          data: {
            submissionViewerEmails: Array.isArray(parsed.submissionViewerEmails) ? parsed.submissionViewerEmails : DEFAULT_FORM16_CONFIG.submissionViewerEmails,
            twentySixAsViewerEmails: Array.isArray(parsed.twentySixAsViewerEmails) ? parsed.twentySixAsViewerEmails : DEFAULT_FORM16_CONFIG.twentySixAsViewerEmails,
            reminderEnabled: typeof parsed.reminderEnabled === 'boolean' ? parsed.reminderEnabled : DEFAULT_FORM16_CONFIG.reminderEnabled,
            reminderDays: typeof parsed.reminderDays === 'number' ? parsed.reminderDays : DEFAULT_FORM16_CONFIG.reminderDays,
            notification26AsDataAdded: merge26As(),
            notificationForm16SuccessCreditNote: mergeNotification('notificationForm16SuccessCreditNote'),
            notificationForm16Unsuccessful: mergeNotification('notificationForm16Unsuccessful'),
            alertSubmitForm16Enabled: typeof parsed.alertSubmitForm16Enabled === 'boolean' ? parsed.alertSubmitForm16Enabled : DEFAULT_FORM16_CONFIG.alertSubmitForm16Enabled,
            alertSubmitForm16FrequencyDays: typeof parsed.alertSubmitForm16FrequencyDays === 'number' ? parsed.alertSubmitForm16FrequencyDays : DEFAULT_FORM16_CONFIG.alertSubmitForm16FrequencyDays,
            alertSubmitForm16FrequencyHours: typeof parsed.alertSubmitForm16FrequencyHours === 'number' ? parsed.alertSubmitForm16FrequencyHours : DEFAULT_FORM16_CONFIG.alertSubmitForm16FrequencyHours,
            alertSubmitForm16RunAtTime: typeof parsed.alertSubmitForm16RunAtTime === 'string' ? (parsed.alertSubmitForm16RunAtTime.trim() ? (/^\d{1,2}:\d{2}$/.test(parsed.alertSubmitForm16RunAtTime.trim()) ? normalizeRunAtTime(parsed.alertSubmitForm16RunAtTime.trim()) : DEFAULT_FORM16_CONFIG.alertSubmitForm16RunAtTime) : '') : DEFAULT_FORM16_CONFIG.alertSubmitForm16RunAtTime,
            alertSubmitForm16Template: typeof parsed.alertSubmitForm16Template === 'string' ? parsed.alertSubmitForm16Template : DEFAULT_FORM16_CONFIG.alertSubmitForm16Template,
            reminderNotificationEnabled: typeof parsed.reminderNotificationEnabled === 'boolean' ? parsed.reminderNotificationEnabled : DEFAULT_FORM16_CONFIG.reminderNotificationEnabled,
            reminderFrequencyDays: typeof parsed.reminderFrequencyDays === 'number' ? parsed.reminderFrequencyDays : DEFAULT_FORM16_CONFIG.reminderFrequencyDays,
            reminderFrequencyHours: typeof parsed.reminderFrequencyHours === 'number' ? parsed.reminderFrequencyHours : DEFAULT_FORM16_CONFIG.reminderFrequencyHours,
            reminderRunAtTime: typeof parsed.reminderRunAtTime === 'string' ? (parsed.reminderRunAtTime.trim() ? (/^\d{1,2}:\d{2}$/.test(parsed.reminderRunAtTime.trim()) ? normalizeRunAtTime(parsed.reminderRunAtTime.trim()) : DEFAULT_FORM16_CONFIG.reminderRunAtTime) : '') : DEFAULT_FORM16_CONFIG.reminderRunAtTime,
            reminderNotificationTemplate: typeof parsed.reminderNotificationTemplate === 'string' ? parsed.reminderNotificationTemplate : DEFAULT_FORM16_CONFIG.reminderNotificationTemplate,
          },
        });
        return;
      } catch {
        // fall through to default
      }
    }
    res.json({ success: true, data: DEFAULT_FORM16_CONFIG });
  } catch (error: any) {
    logger.error('[Admin] Error fetching Form 16 config:', error);
    res.status(500).json({
      success: false,
      error: error.message || 'Failed to fetch Form 16 configuration',
    });
  }
};

/**
 * Update Form 16 admin configuration
 */
export const putForm16Config = async (req: Request, res: Response): Promise<void> => {
  try {
    const userId = req.user?.userId;
    if (!userId) {
      res.status(401).json({ success: false, error: 'User not authenticated' });
      return;
    }
    const body = sanitizeObject(req.body as Record<string, unknown>);
    const normalizeEmail = (e: unknown) => String(e ?? '').trim().toLowerCase();
    const submissionViewerEmails = Array.isArray(body.submissionViewerEmails)
      ? body.submissionViewerEmails.map(normalizeEmail).filter(Boolean)
      : DEFAULT_FORM16_CONFIG.submissionViewerEmails;
    const twentySixAsViewerEmails = Array.isArray(body.twentySixAsViewerEmails)
      ? body.twentySixAsViewerEmails.map(normalizeEmail).filter(Boolean)
      : DEFAULT_FORM16_CONFIG.twentySixAsViewerEmails;
    const reminderEnabled = typeof body.reminderEnabled === 'boolean' ? body.reminderEnabled : DEFAULT_FORM16_CONFIG.reminderEnabled;
    const reminderDays = typeof body.reminderDays === 'number' ? body.reminderDays : DEFAULT_FORM16_CONFIG.reminderDays;
    const mergeNotif = (key: keyof typeof DEFAULT_FORM16_CONFIG) => {
      const v = body[key];
      const d = DEFAULT_FORM16_CONFIG[key] as any;
      if (v && typeof v === 'object' && typeof (v as any).enabled === 'boolean') {
        return { enabled: (v as any).enabled, template: typeof (v as any).template === 'string' ? (v as any).template : (d?.template ?? '') };
      }
      return d;
    };
    const d26As = DEFAULT_FORM16_CONFIG.notification26AsDataAdded as { enabled: boolean; templateRe: string; templateDealers: string };
    const merge26As = () => {
      const v = body.notification26AsDataAdded;
      if (v && typeof v === 'object' && typeof (v as any).enabled === 'boolean') {
        return {
          enabled: (v as any).enabled,
          templateRe: typeof (v as any).templateRe === 'string' ? (v as any).templateRe : d26As.templateRe,
          templateDealers: typeof (v as any).templateDealers === 'string' ? (v as any).templateDealers : d26As.templateDealers,
        };
      }
      return d26As;
    };
    const configValue = JSON.stringify({
      submissionViewerEmails,
      twentySixAsViewerEmails,
      reminderEnabled,
      reminderDays,
      notification26AsDataAdded: merge26As(),
      notificationForm16SuccessCreditNote: mergeNotif('notificationForm16SuccessCreditNote'),
      notificationForm16Unsuccessful: mergeNotif('notificationForm16Unsuccessful'),
      alertSubmitForm16Enabled: typeof body.alertSubmitForm16Enabled === 'boolean' ? body.alertSubmitForm16Enabled : DEFAULT_FORM16_CONFIG.alertSubmitForm16Enabled,
      alertSubmitForm16FrequencyDays: typeof body.alertSubmitForm16FrequencyDays === 'number' ? body.alertSubmitForm16FrequencyDays : DEFAULT_FORM16_CONFIG.alertSubmitForm16FrequencyDays,
      alertSubmitForm16FrequencyHours: typeof body.alertSubmitForm16FrequencyHours === 'number' ? body.alertSubmitForm16FrequencyHours : DEFAULT_FORM16_CONFIG.alertSubmitForm16FrequencyHours,
      alertSubmitForm16RunAtTime: typeof body.alertSubmitForm16RunAtTime === 'string' ? (String(body.alertSubmitForm16RunAtTime).trim() ? (/^\d{1,2}:\d{2}$/.test(String(body.alertSubmitForm16RunAtTime).trim()) ? normalizeRunAtTime(String(body.alertSubmitForm16RunAtTime).trim()) : DEFAULT_FORM16_CONFIG.alertSubmitForm16RunAtTime) : '') : DEFAULT_FORM16_CONFIG.alertSubmitForm16RunAtTime,
      alertSubmitForm16Template: typeof body.alertSubmitForm16Template === 'string' ? body.alertSubmitForm16Template : DEFAULT_FORM16_CONFIG.alertSubmitForm16Template,
      reminderNotificationEnabled: typeof body.reminderNotificationEnabled === 'boolean' ? body.reminderNotificationEnabled : DEFAULT_FORM16_CONFIG.reminderNotificationEnabled,
      reminderFrequencyDays: typeof body.reminderFrequencyDays === 'number' ? body.reminderFrequencyDays : DEFAULT_FORM16_CONFIG.reminderFrequencyDays,
      reminderFrequencyHours: typeof body.reminderFrequencyHours === 'number' ? body.reminderFrequencyHours : DEFAULT_FORM16_CONFIG.reminderFrequencyHours,
      reminderRunAtTime: typeof body.reminderRunAtTime === 'string' ? (String(body.reminderRunAtTime).trim() ? (/^\d{1,2}:\d{2}$/.test(String(body.reminderRunAtTime).trim()) ? normalizeRunAtTime(String(body.reminderRunAtTime).trim()) : DEFAULT_FORM16_CONFIG.reminderRunAtTime) : '') : DEFAULT_FORM16_CONFIG.reminderRunAtTime,
      reminderNotificationTemplate: typeof body.reminderNotificationTemplate === 'string' ? body.reminderNotificationTemplate : DEFAULT_FORM16_CONFIG.reminderNotificationTemplate,
    });

    await sequelize.query(
      `INSERT INTO admin_configurations (
        config_id, config_key, config_category, config_value, value_type, display_name, description, is_editable, is_sensitive, sort_order, created_at, updated_at, last_modified_by, last_modified_at
      ) VALUES (
        gen_random_uuid(), :configKey, 'SYSTEM_SETTINGS', :configValue, 'JSON', 'Form 16 Admin Config', 'Form 16 visibility and reminder settings', true, false, 0, NOW(), NOW(), :userId, NOW()
      )
      ON CONFLICT (config_key) DO UPDATE SET
        config_value = EXCLUDED.config_value,
        last_modified_by = EXCLUDED.last_modified_by,
        last_modified_at = NOW(),
        updated_at = NOW()`,
      {
        replacements: { configKey: FORM16_CONFIG_KEY, configValue, userId },
        type: QueryTypes.RAW,
      }
    );
    clearConfigCache();
    logger.info('[Admin] Form 16 configuration updated');
    res.json({ success: true, message: 'Form 16 configuration saved' });
  } catch (error: any) {
    logger.error('[Admin] Error updating Form 16 config:', error);
    res.status(500).json({
      success: false,
      error: error.message || 'Failed to save Form 16 configuration',
    });
  }
};

/**
 * Get CPC-CSD admin configuration (who can access CPC-CSD module).
 */
export const getCpcCdcConfig = async (req: Request, res: Response): Promise<void> => {
  try {
    const raw = await selectCpcCsdAdminConfigValue();

    if (raw) {
      try {
        const parsed = JSON.parse(raw);
        res.json({
          success: true,
          data: {
            viewerEmails: Array.isArray(parsed.viewerEmails) ? parsed.viewerEmails : DEFAULT_CPC_CSD_CONFIG.viewerEmails,
          },
        });
        return;
      } catch {
        // fall through to defaults
      }
    }

    res.json({ success: true, data: DEFAULT_CPC_CSD_CONFIG });
  } catch (error: any) {
    logger.error('[Admin] Error fetching CPC-CSD config:', error);
    res.status(500).json({
      success: false,
      error: error.message || 'Failed to fetch CPC-CSD configuration',
    });
  }
};

/**
 * Update CPC-CSD admin configuration.
 */
export const putCpcCdcConfig = async (req: Request, res: Response): Promise<void> => {
  try {
    const userId = req.user?.userId;
    if (!userId) {
      res.status(401).json({ success: false, error: 'User not authenticated' });
      return;
    }

    const body = sanitizeObject(req.body as Record<string, unknown>);
    const normalizeEmail = (e: unknown) => String(e ?? '').trim().toLowerCase();
    const viewerEmails = Array.isArray(body.viewerEmails)
      ? body.viewerEmails.map(normalizeEmail).filter(Boolean)
      : DEFAULT_CPC_CSD_CONFIG.viewerEmails;

    const configValue = JSON.stringify({
      viewerEmails,
    });

    await sequelize.query(
      `INSERT INTO admin_configurations (
        config_id, config_key, config_category, config_value, value_type, display_name, description, is_editable, is_sensitive, sort_order, created_at, updated_at, last_modified_by, last_modified_at
      ) VALUES (
        gen_random_uuid(), :configKey, 'SYSTEM_SETTINGS', :configValue, 'JSON', 'CPC-CSD Admin Config', 'CPC-CSD module visibility settings', true, false, 0, NOW(), NOW(), :userId, NOW()
      )
      ON CONFLICT (config_key) DO UPDATE SET
        config_value = EXCLUDED.config_value,
        last_modified_by = EXCLUDED.last_modified_by,
        last_modified_at = NOW(),
        updated_at = NOW()`,
      {
        replacements: { configKey: CPC_CSD_ADMIN_CONFIG_KEY, configValue, userId },
        type: QueryTypes.RAW,
      }
    );

    await sequelize.query(
      `DELETE FROM admin_configurations WHERE config_key = :legacy`,
      { replacements: { legacy: CPC_CDC_ADMIN_CONFIG_KEY_LEGACY }, type: QueryTypes.RAW }
    );

    clearConfigCache();
    logger.info('[Admin] CPC-CSD configuration updated');
    res.json({ success: true, message: 'CPC-CSD configuration saved' });
  } catch (error: any) {
    logger.error('[Admin] Error updating CPC-CSD config:', error);
    res.status(500).json({
      success: false,
      error: error.message || 'Failed to save CPC-CSD configuration',
    });
  }
};

/**
 * ============================================
 * USER ROLE MANAGEMENT (RBAC)
 * ============================================
 */

/**
 * Update User Role
 * 
 * Purpose: Change user's role (USER, MANAGEMENT, ADMIN)
 * 
 * Access: ADMIN only
 * 
 * Body: { role: 'USER' | 'MANAGEMENT' | 'ADMIN' }
 */
export const updateUserRole = async (req: Request, res: Response): Promise<void> => {
  try {
    const { userId } = req.params;
    const { role } = req.body;

    // Validate role
    const validRoles: UserRole[] = ['USER', 'MANAGEMENT', 'ADMIN'];
    if (!role || !validRoles.includes(role)) {
      res.status(400).json({
        success: false,
        error: 'Invalid role. Must be USER, MANAGEMENT, or ADMIN'
      });
      return;
    }

    // Find user
    const user = await User.findByPk(userId);
    if (!user) {
      res.status(404).json({
        success: false,
        error: 'User not found'
      });
      return;
    }

    // Store old role for logging
    const oldRole = user.role;

    // Prevent self-demotion from ADMIN (safety check)
    const adminUser = req.user;
    if (adminUser?.userId === userId && role !== 'ADMIN') {
      res.status(400).json({
        success: false,
        error: 'Cannot remove your own admin privileges. Ask another admin to change your role.'
      });
      return;
    }

    // Update role
    user.role = role;
    await user.save();

    logger.info(`✅ User role updated by ${adminUser?.email}: ${user.email} - ${oldRole} → ${role}`);

    res.json({
      success: true,
      message: `User role updated from ${oldRole} to ${role}`,
      data: {
        userId: user.userId,
        email: user.email,
        displayName: user.displayName,
        role: user.role,
        previousRole: oldRole,
        updatedAt: user.updatedAt
      }
    });
  } catch (error) {
    logger.error('[Admin] Error updating user role:', error);
    res.status(500).json({
      success: false,
      error: 'Failed to update user role'
    });
  }
};

/**
 * Get All Users by Role (with pagination and filtering)
 * 
 * Purpose: List all users with optional role filtering and pagination
 * 
 * Access: ADMIN only
 * 
 * Query: 
 * - ?role=ADMIN | MANAGEMENT | USER | ALL | ELEVATED (default: ELEVATED for ADMIN+MANAGEMENT only)
 * - ?page=1 (default)
 * - ?limit=10 (default)
 */
export const getUsersByRole = async (req: Request, res: Response): Promise<void> => {
  try {
    const { role, page = '1', limit = '10' } = req.query;

    const pageNum = parseInt(page as string) || 1;
    const limitNum = Math.min(parseInt(limit as string) || 10, 100); // Max 100 per page
    const offset = (pageNum - 1) * limitNum;

    const whereClause: any = { isActive: true };

    // Handle role filtering
    if (role && role !== 'ALL' && role !== 'ELEVATED') {
      const validRoles: UserRole[] = ['USER', 'MANAGEMENT', 'ADMIN'];
      if (!validRoles.includes(role as UserRole)) {
        res.status(400).json({
          success: false,
          error: 'Invalid role. Must be USER, MANAGEMENT, ADMIN, ALL, or ELEVATED'
        });
        return;
      }
      whereClause.role = role;
    } else if (role === 'ELEVATED' || !role) {
      // Default: Show only ADMIN and MANAGEMENT (elevated users)
      whereClause.role = { [Op.in]: ['ADMIN', 'MANAGEMENT'] };
    }
    // If role === 'ALL', don't filter by role (show all users)

    // Get total count for pagination
    const totalUsers = await User.count({ where: whereClause });
    const totalPages = Math.ceil(totalUsers / limitNum);

    // Get paginated users
    const users = await User.findAll({
      where: whereClause,
      attributes: [
        'userId',
        'email',
        'displayName',
        'firstName',
        'lastName',
        'department',
        'designation',
        'role',
        'manager',
        'postalAddress',
        'lastLogin',
        'createdAt'
      ],
      order: [
        ['role', 'ASC'], // ADMIN first, then MANAGEMENT, then USER
        ['displayName', 'ASC']
      ],
      limit: limitNum,
      offset: offset
    });

    // Get role summary (across all users, not just current page)
    const roleStats = await sequelize.query(`
      SELECT 
        role,
        COUNT(*) as count
      FROM users
      WHERE is_active = true
      GROUP BY role
      ORDER BY 
        CASE role
          WHEN 'ADMIN' THEN 1
          WHEN 'MANAGEMENT' THEN 2
          WHEN 'USER' THEN 3
        END
    `, {
      type: QueryTypes.SELECT
    });

    const summary = {
      ADMIN: parseInt((roleStats.find((s: any) => s.role === 'ADMIN') as any)?.count || '0'),
      MANAGEMENT: parseInt((roleStats.find((s: any) => s.role === 'MANAGEMENT') as any)?.count || '0'),
      USER: parseInt((roleStats.find((s: any) => s.role === 'USER') as any)?.count || '0')
    };

    res.json({
      success: true,
      data: {
        users: users,
        pagination: {
          currentPage: pageNum,
          totalPages: totalPages,
          totalUsers: totalUsers,
          limit: limitNum,
          hasNextPage: pageNum < totalPages,
          hasPrevPage: pageNum > 1
        },
        summary,
        filter: role || 'ELEVATED'
      }
    });
  } catch (error) {
    logger.error('[Admin] Error fetching users by role:', error);
    res.status(500).json({
      success: false,
      error: 'Failed to fetch users'
    });
  }
};

/**
 * Get Role Statistics
 * 
 * Purpose: Get count of users in each role
 * 
 * Access: ADMIN only
 */
export const getRoleStatistics = async (req: Request, res: Response): Promise<void> => {
  try {
    const stats = await sequelize.query(`
      SELECT 
        role,
        COUNT(*) as count,
        COUNT(CASE WHEN is_active = true THEN 1 END) as active_count,
        COUNT(CASE WHEN is_active = false THEN 1 END) as inactive_count
      FROM users
      GROUP BY role
      ORDER BY 
        CASE role
          WHEN 'ADMIN' THEN 1
          WHEN 'MANAGEMENT' THEN 2
          WHEN 'USER' THEN 3
        END
    `, {
      type: QueryTypes.SELECT
    });

    res.json({
      success: true,
      data: {
        statistics: stats,
        total: stats.reduce((sum: number, stat: any) => sum + parseInt(stat.count), 0)
      }
    });
  } catch (error) {
    logger.error('[Admin] Error fetching role statistics:', error);
    res.status(500).json({
      success: false,
      error: 'Failed to fetch role statistics'
    });
  }
};

/**
 * Assign role to user by email
 * 
 * Purpose: Search user in Okta, create if doesn't exist, then assign role
 * 
 * Access: ADMIN only
 * 
 * Body: { email: string, role: 'USER' | 'MANAGEMENT' | 'ADMIN' }
 */
export const assignRoleByEmail = async (req: Request, res: Response): Promise<void> => {
  try {
    const { email, role } = req.body;
    const currentUserId = req.user?.userId;

    // Validate inputs
    if (!email || !role) {
      res.status(400).json({
        success: false,
        error: 'Email and role are required'
      });
      return;
    }

    // Validate role
    if (!['USER', 'MANAGEMENT', 'ADMIN'].includes(role)) {
      res.status(400).json({
        success: false,
        error: 'Invalid role. Must be USER, MANAGEMENT, or ADMIN'
      });
      return;
    }

    logger.info(`[Admin] Assigning role ${role} to ${email} by user ${currentUserId}`);

    // First, check if user already exists in our database
    let user = await User.findOne({ where: { email } });

    if (!user) {
      // User doesn't exist, need to fetch from Okta and create
      logger.info(`[Admin] User ${email} not found in database, fetching from Okta...`);

      // Import UserService to fetch full profile from Okta
      const { UserService } = await import('@services/user.service');
      const userService = new UserService();

      try {
        // Fetch full user profile from Okta Users API (includes manager, jobTitle, etc.)
        const oktaUserData = await userService.fetchAndExtractOktaUserByEmail(email);

        if (!oktaUserData) {
          res.status(404).json({
            success: false,
            error: 'User not found in Okta. Please ensure the email is correct.'
          });
          return;
        }

        // Create user in our database via centralized userService with all fields including manager
        const ensured = await userService.createOrUpdateUser({
          ...oktaUserData,
          role, // Set the assigned role
          isActive: true, // Ensure user is active
        });
        user = ensured;

        logger.info(`[Admin] Created new user ${email} with role ${role} (manager: ${oktaUserData.manager || 'N/A'})`);
      } catch (oktaError: any) {
        logger.error('[Admin] Error fetching from Okta:', oktaError);
        res.status(500).json({
          success: false,
          error: 'Failed to fetch user from Okta: ' + (oktaError.message || 'Unknown error')
        });
        return;
      }
    } else {
      // User exists - fetch latest data from Okta and sync all fields including role
      const previousRole = user.role;

      // Prevent self-demotion
      if (user.userId === currentUserId && role !== 'ADMIN') {
        res.status(403).json({
          success: false,
          error: 'You cannot demote yourself from ADMIN role'
        });
        return;
      }

      // Import UserService to fetch latest data from Okta
      const { UserService } = await import('@services/user.service');
      const userService = new UserService();

      try {
        // Fetch full user profile from Okta Users API to sync manager and other fields
        const oktaUserData = await userService.fetchAndExtractOktaUserByEmail(email);

        if (oktaUserData) {
          // Sync all fields from Okta including the new role using centralized method
          const updated = await userService.createOrUpdateUser({
            ...oktaUserData, // Includes all fields: manager, jobTitle, postalAddress, etc.
            role, // Set the new role
            isActive: true, // Ensure user is active
          });
          user = updated;

          logger.info(`[Admin] Synced user ${email} from Okta (manager: ${oktaUserData.manager || 'N/A'}) and updated role from ${previousRole} to ${role}`);
        } else {
          // Okta user not found, just update role
          await user.update({ role });
          logger.info(`[Admin] Updated user ${email} role from ${previousRole} to ${role} (Okta data not available)`);
        }
      } catch (oktaError: any) {
        // If Okta fetch fails, just update the role
        logger.warn(`[Admin] Failed to fetch Okta data for ${email}, updating role only:`, oktaError.message);
        await user.update({ role });
        logger.info(`[Admin] Updated user ${email} role from ${previousRole} to ${role} (Okta sync failed)`);
      }
    }

    res.json({
      success: true,
      message: `Successfully assigned ${role} role to ${user.displayName || email}`,
      data: {
        userId: user.userId,
        email: user.email,
        displayName: user.displayName,
        role: user.role
      }
    });
  } catch (error) {
    logger.error('[Admin] Error assigning role by email:', error);
    res.status(500).json({
      success: false,
      error: 'Failed to assign role'
    });
  }
};

// ==================== Activity Type Management Routes ====================

/**
 * Get all activity types (optionally filtered by active status)
 */
export const getAllActivityTypes = async (req: Request, res: Response): Promise<void> => {
  try {
    const { activeOnly } = req.query;
    const activeOnlyBool = activeOnly === 'true';

    const activityTypes = await activityTypeService.getAllActivityTypes(activeOnlyBool);

    res.json({
      success: true,
      data: activityTypes,
      count: activityTypes.length
    });
  } catch (error: any) {
    logger.error('[Admin] Error fetching activity types:', error);
    res.status(500).json({
      success: false,
      error: error.message || 'Failed to fetch activity types'
    });
  }
};

/**
 * Get a single activity type by ID
 */
export const getActivityTypeById = async (req: Request, res: Response): Promise<void> => {
  try {
    const { activityTypeId } = req.params;

    const activityType = await activityTypeService.getActivityTypeById(activityTypeId);

    if (!activityType) {
      res.status(404).json({
        success: false,
        error: 'Activity type not found'
      });
      return;
    }

    res.json({
      success: true,
      data: activityType
    });
  } catch (error: any) {
    logger.error('[Admin] Error fetching activity type:', error);
    res.status(500).json({
      success: false,
      error: error.message || 'Failed to fetch activity type'
    });
  }
};

/**
 * Create a new activity type
 */
export const createActivityType = async (req: Request, res: Response): Promise<void> => {
  try {
    const userId = req.user?.userId;
    if (!userId) {
      res.status(401).json({
        success: false,
        error: 'User not authenticated'
      });
      return;
    }

    const {
      title,
      itemCode,
      taxationType,
      sapRefNo
    } = req.body;

    // Validate required fields
    if (!title) {
      res.status(400).json({
        success: false,
        error: 'Activity type title is required'
      });
      return;
    }

    const activityType = await activityTypeService.createActivityType({
      title,
      itemCode: itemCode || null,
      taxationType: taxationType || null,
      sapRefNo: sapRefNo || null,
      creditPostingOn: req.body.creditPostingOn || null,
      createdBy: userId
    });

    res.status(201).json({
      success: true,
      message: 'Activity type created successfully',
      data: activityType
    });
  } catch (error: any) {
    logger.error('[Admin] Error creating activity type:', error);
    res.status(500).json({
      success: false,
      error: error.message || 'Failed to create activity type'
    });
  }
};

/**
 * Update an activity type
 */
export const updateActivityType = async (req: Request, res: Response): Promise<void> => {
  try {
    const userId = req.user?.userId;
    if (!userId) {
      res.status(401).json({
        success: false,
        error: 'User not authenticated'
      });
      return;
    }

    const { activityTypeId } = req.params;
    const updates = req.body;

    const activityType = await activityTypeService.updateActivityType(activityTypeId, updates, userId);

    if (!activityType) {
      res.status(404).json({
        success: false,
        error: 'Activity type not found'
      });
      return;
    }

    res.json({
      success: true,
      message: 'Activity type updated successfully',
      data: activityType
    });
  } catch (error: any) {
    logger.error('[Admin] Error updating activity type:', error);
    res.status(500).json({
      success: false,
      error: error.message || 'Failed to update activity type'
    });
  }
};

/**
 * Delete (deactivate) an activity type
 */
export const deleteActivityType = async (req: Request, res: Response): Promise<void> => {
  try {
    const { activityTypeId } = req.params;

    await activityTypeService.deleteActivityType(activityTypeId);

    res.json({
      success: true,
      message: 'Activity type deleted successfully'
    });
  } catch (error: any) {
    logger.error('[Admin] Error deleting activity type:', error);
    res.status(500).json({
      success: false,
      error: error.message || 'Failed to delete activity type'
    });
  }
};

