import { z } from 'zod';
import * as xlsx from 'xlsx';
import { v4 as uuidv4 } from 'uuid';
import { drawEngine, calculateUserTags } from './draw.engine';
import { logger } from '../utils/logger';
import { redis } from '../config/redis';
import type { UserTag } from '@gala/shared/constants';

// Redis key for storing participants
const PARTICIPANTS_KEY = 'gala:participants:list';
// Types
// ============================================================================

export interface Participant {
    id: string;
    name: string;
    position: string; // 岗位
    birthYear: number;
    tags: UserTag[];
}

export interface ImportResult {
    success: boolean;
    totalCount: number;
    importedCount: number;
    tagDistribution: Record<string, number>;
    errors: string[];
    participants: Participant[];
}

// ============================================================================
// Excel Column Mapping
// ============================================================================

// Expected columns: 岗位, 姓名, 年份
const COLUMN_MAPPING = {
    position: ['岗位', '职位', 'Position', 'position'],
    name: ['姓名', '名字', 'Name', 'name'],
    birthYear: ['年份', '出生年份', 'Year', 'year', '生日'],
};

function findColumnIndex(headers: string[], possibleNames: string[]): number {
    for (const name of possibleNames) {
        const index = headers.findIndex(
            (h) => h && h.toString().trim().toLowerCase() === name.toLowerCase()
        );
        if (index !== -1) return index;
    }
    return -1;
}

// ============================================================================
// Participant Service
// ============================================================================

class ParticipantService {
    private participants: Map<string, Participant> = new Map();

    /**
     * Parse Excel buffer and import participants
     */
    async importFromExcel(buffer: Buffer): Promise<ImportResult> {
        const errors: string[] = [];
        const participants: Participant[] = [];
        const tagDistribution: Record<string, number> = {};

        try {
            // Parse Excel
            const workbook = xlsx.read(buffer, { type: 'buffer' });
            const sheetName = workbook.SheetNames[0];
            const sheet = workbook.Sheets[sheetName];

            // Convert to JSON with header row
            const data = xlsx.utils.sheet_to_json<string[]>(sheet, { header: 1 });

            if (data.length < 2) {
                return {
                    success: false,
                    totalCount: 0,
                    importedCount: 0,
                    tagDistribution: {},
                    errors: ['Excel 文件为空或只有表头'],
                    participants: [],
                };
            }

            // Find column indices
            const headers = data[0] as string[];
            const positionIndex = findColumnIndex(headers, COLUMN_MAPPING.position);
            const nameIndex = findColumnIndex(headers, COLUMN_MAPPING.name);
            const yearIndex = findColumnIndex(headers, COLUMN_MAPPING.birthYear);

            if (positionIndex === -1) {
                errors.push('未找到"岗位"列');
            }
            if (nameIndex === -1) {
                errors.push('未找到"姓名"列');
            }
            if (yearIndex === -1) {
                errors.push('未找到"年份"列');
            }

            if (errors.length > 0) {
                return {
                    success: false,
                    totalCount: data.length - 1,
                    importedCount: 0,
                    tagDistribution: {},
                    errors,
                    participants: [],
                };
            }

            // Process rows
            for (let i = 1; i < data.length; i++) {
                const row = data[i];
                if (!row || row.length === 0) continue;

                const position = row[positionIndex]?.toString().trim() || '';
                const name = row[nameIndex]?.toString().trim() || '';
                const yearValue = row[yearIndex];

                if (!name) {
                    errors.push(`第 ${i + 1} 行: 姓名为空`);
                    continue;
                }

                let birthYear: number;
                if (typeof yearValue === 'number') {
                    birthYear = yearValue;
                } else {
                    birthYear = parseInt(yearValue?.toString().trim() || '', 10);
                }

                if (isNaN(birthYear) || birthYear < 1940 || birthYear > 2010) {
                    errors.push(`第 ${i + 1} 行 (${name}): 年份无效 (${yearValue})`);
                    continue;
                }

                // Calculate tags using existing draw engine logic
                // Convert year to birth date string format (YYYY-01-01)
                const tags = calculateUserTags(`${birthYear}-01-01`);

                const participant: Participant = {
                    id: uuidv4(),
                    name,
                    position,
                    birthYear,
                    tags,
                };

                participants.push(participant);

                // Update tag distribution
                for (const tag of tags) {
                    tagDistribution[tag] = (tagDistribution[tag] || 0) + 1;
                }
            }

            // Store in memory
            this.participants.clear();
            for (const p of participants) {
                this.participants.set(p.id, p);
            }

            // Persist to Redis
            await this.saveToRedis();

            // Initialize draw pools in Redis
            await drawEngine.initializePools(
                participants.map((p) => ({
                    userId: p.id,
                    birthDate: `${p.birthYear}-01-01`,
                }))
            );

            logger.info(
                {
                    totalCount: data.length - 1,
                    importedCount: participants.length,
                    tagDistribution,
                },
                'Participants imported successfully'
            );

            return {
                success: true,
                totalCount: data.length - 1,
                importedCount: participants.length,
                tagDistribution,
                errors,
                participants,
            };
        } catch (error) {
            logger.error({ error }, 'Failed to import participants from Excel');
            return {
                success: false,
                totalCount: 0,
                importedCount: 0,
                tagDistribution: {},
                errors: [`解析错误: ${(error as Error).message}`],
                participants: [],
            };
        }
    }

    /**
     * Get all participants
     */
    getAll(): Participant[] {
        return Array.from(this.participants.values());
    }

    /**
     * Get participant by ID
     */
    getById(id: string): Participant | undefined {
        return this.participants.get(id);
    }

    /**
     * Get participants by tag
     */
    getByTag(tag: UserTag): Participant[] {
        return this.getAll().filter((p) => p.tags.includes(tag));
    }

    /**
     * Get participant count
     */
    getCount(): number {
        return this.participants.size;
    }

    /**
     * Save participants to Redis for persistence
     */
    private async saveToRedis(): Promise<void> {
        try {
            const participants = this.getAll();
            if (participants.length === 0) {
                await redis.del(PARTICIPANTS_KEY);
                return;
            }
            await redis.set(PARTICIPANTS_KEY, JSON.stringify(participants));
            logger.info({ count: participants.length }, 'Participants saved to Redis');
        } catch (error) {
            logger.error({ error }, 'Failed to save participants to Redis');
        }
    }

    /**
     * Restore participants from Redis on server start
     */
    async restoreFromRedis(): Promise<void> {
        try {
            const data = await redis.get(PARTICIPANTS_KEY);
            if (!data) {
                logger.info('No participants found in Redis');
                return;
            }

            const participants: Participant[] = JSON.parse(data);
            this.participants.clear();
            for (const p of participants) {
                this.participants.set(p.id, p);
            }

            // Re-initialize draw pools
            if (participants.length > 0) {
                await drawEngine.initializePools(
                    participants.map((p) => ({
                        userId: p.id,
                        birthDate: `${p.birthYear}-01-01`,
                    }))
                );
            }

            logger.info({ count: participants.length }, 'Participants restored from Redis');
        } catch (error) {
            logger.error({ error }, 'Failed to restore participants from Redis');
        }
    }

    /**
     * Clear all participants (for admin reset)
     */
    async clear(): Promise<void> {
        this.participants.clear();
        await redis.del(PARTICIPANTS_KEY);
        logger.info('Participants cleared');
    }
}

export const participantService = new ParticipantService();
