import { config } from '../../config/index.js';
import { IRule, MarketContext, RuleResult, SignalDirection } from './types.js';

export class SessionRule implements IRule {
    public name = 'SessionRule';
    private readonly defaultAllowedSessions = ['LDN', 'NY'];
    private readonly allSessions = ['LDN', 'NY', 'TOK', 'SYD'];

    public isEnabled(): boolean {
        return config.PRO_STRATEGY.ENABLED_RULES.includes('session');
    }

    public async check(context: MarketContext, params?: Record<string, any>): Promise<RuleResult> {
        const sessionName = context.session;
        const activeSessions = sessionName === 'OFF'
            ? []
            : sessionName.split('|').map(s => s.trim()).filter(Boolean);
        const allowedSessions = this.normalizeAllowedSessions(params?.sessions ?? params?.allowedSessions);

        // ── 24/7 Mode Detection ─────────────────────────────────────────────
        // If all four major sessions are allowed, it means the profile is set
        // to trade 24/7. Skip all session-time checks and always pass.
        const is24h = this.allSessions.every(s => allowedSessions.includes(s));
        if (is24h) {
            return {
                ruleName: this.name,
                passed: true,
                signal: SignalDirection.NONE,
                reason: `24/7 mode: all sessions allowed. Current session: ${sessionName}.`
            };
        }

        // ── Restricted Session Mode ──────────────────────────────────────────
        // The profile only allows a subset of sessions (e.g. LDN,NY only).
        // Pass if the active session is explicitly in the allowed list.
        if (sessionName === 'OFF') {
            return {
                ruleName: this.name,
                passed: false,
                signal: SignalDirection.NONE,
                reason: 'Off-session blocked by SessionRule.'
            };
        }

        const hasAllowedSession = activeSessions.some(s => allowedSessions.includes(s));
        if (hasAllowedSession) {
            return {
                ruleName: this.name,
                passed: true,
                signal: SignalDirection.NONE,
                reason: `Session ${sessionName} is in allowed set (${allowedSessions.join(', ')}).`
            };
        }

        return {
            ruleName: this.name,
            passed: false,
            signal: SignalDirection.NONE,
            reason: `Session ${sessionName} not in allowed set (${allowedSessions.join(', ')}).`
        };
    }

    private normalizeAllowedSessions(raw: unknown): string[] {
        if (raw === undefined || raw === null) {
            return this.defaultAllowedSessions;
        }

        // ── Literal '24/7' shorthand ──────────────────────────────────
        // The UI stores '24/7' as a literal string when the user selects
        // "Trade 24/7". Expand it to all four session codes immediately.
        const rawTokens = (Array.isArray(raw)
            ? raw.map(v => String(v))
            : String(raw).split(/[,\|]/))
            .map(token => token.trim())
            .filter(Boolean);
        if (rawTokens.length === 0) {
            return this.defaultAllowedSessions;
        }

        const normalizedTokens = rawTokens
            .map(token => this.normalizeSessionToken(token))
            .filter(Boolean) as string[];

        const has24x7Token = normalizedTokens.some(token => this.isAllSessionsToken(token));
        if (has24x7Token) {
            return [...this.allSessions];
        }

        const aliases: Record<string, string> = {
            LONDON: 'LDN',
            LDN: 'LDN',
            NEWYORK: 'NY',
            NY: 'NY',
            TOKYO: 'TOK',
            TOK: 'TOK',
            SYDNEY: 'SYD',
            SYD: 'SYD'
        };

        const normalized = normalizedTokens
            .map(s => aliases[s] || s)
            .filter(Boolean);

        const deduped = Array.from(new Set(normalized));
        return deduped.length > 0 ? deduped : this.defaultAllowedSessions;
    }

    private isAllSessionsToken(token: string): boolean {
        const compact = String(token || '').replace(/[^A-Z0-9]/g, '');
        return compact === '247'
            || compact === '24X7'
            || compact === 'ALL'
            || compact === 'ALLSESSIONS';
    }

    private normalizeSessionToken(rawToken: string): string {
        return String(rawToken || '')
            .normalize('NFKC')
            .toUpperCase()
            .replace(/[⁄∕／\u2215\u2044\u29F8\uFF0F]/g, '/')
            .replace(/[×✕✖✗]/g, 'X')
            .replace(/[\s_-]+/g, '')
            .trim();
    }
}
