import { createHash } from 'crypto';
import { config } from '../config/index.js';

export type AlpacaSubTagIntent = 'ENTRY' | 'EXIT' | 'UNKNOWN';

export interface BuildAlpacaSubTagInput {
    profileId: string;
    tradeId?: string;
    intent?: AlpacaSubTagIntent;
    envLabel?: string;
    maxLength?: number;
}

type ParsedCompactSubTag = {
    format: 'compact';
    raw: string;
    env: string;
    profileToken: string;
    tradeToken: string;
    intent: string;
};

type ParsedLegacySubTag = {
    format: 'legacy';
    raw: string;
    env: string;
    profileSegment: string;
    tradeSegment: string;
};

type ParsedSubTag = ParsedCompactSubTag | ParsedLegacySubTag;

const SUBTAG_PREFIX = 'BL';

const cleanToken = (value: unknown, maxLen: number): string => {
    const normalized = String(value || '')
        .trim()
        .toUpperCase()
        .replace(/[^A-Z0-9_-]/g, '');
    if (!normalized) return '';
    return normalized.slice(0, Math.max(1, maxLen));
};

const hashToken = (value: unknown, length: number): string => {
    const digest = createHash('sha1')
        .update(String(value || '').trim())
        .digest('hex')
        .toUpperCase();
    return digest.slice(0, Math.max(1, length));
};

const normalizeProfileId = (profileId: unknown): string => String(profileId || '').trim();

const resolveEnvToken = (inputEnv?: string): string => {
    const explicit = cleanToken(inputEnv || config.ALPACA_SUBTAG_ENV || '', 12);
    if (explicit) return explicit;
    return config.PAPER_TRADING ? 'PAPER' : 'LIVE';
};

const resolveMaxLength = (override?: number): number => {
    const parsed = Number(override ?? config.ALPACA_SUBTAG_MAX_LENGTH);
    if (!Number.isFinite(parsed) || parsed < 24) return 48;
    return Math.floor(parsed);
};

const parseAllowlist = (): Set<string> => {
    const values = Array.isArray(config.ALPACA_OMNIBUS_PROFILE_ALLOWLIST)
        ? config.ALPACA_OMNIBUS_PROFILE_ALLOWLIST
        : [];
    return new Set(values.map((item) => normalizeProfileId(item)).filter(Boolean));
};

const parseDisabledExecutionExchanges = (): Set<string> => {
    const values = Array.isArray(config.ALPACA_SUBTAG_DISABLE_FOR_EXCHANGE)
        ? config.ALPACA_SUBTAG_DISABLE_FOR_EXCHANGE
        : [];
    return new Set(
        values
            .map((item) => String(item || '').trim().toLowerCase())
            .filter(Boolean)
    );
};

const resolveExecutionExchangeKeys = (): string[] => {
    const provider = String(config.EXECUTION_PROVIDER || '').trim().toLowerCase();
    if (!provider) return [];
    if (provider !== 'ccxt') return [provider];

    const exchange = String(config.EXCHANGE || '').trim().toLowerCase();
    return exchange ? [provider, exchange] : [provider];
};

const isTruthyFlag = (value: unknown): boolean => {
    if (typeof value === 'boolean') return value;
    if (typeof value === 'string') {
        const normalized = value.trim().toLowerCase();
        return ['true', '1', 'yes', 'on', 'y'].includes(normalized);
    }
    if (typeof value === 'number') return value === 1;
    return false;
};

const resolveOmnibusFromProfile = (profileSettings?: any): boolean => {
    if (!profileSettings || typeof profileSettings !== 'object') return false;

    const execution = profileSettings?.strategy_config?.execution || {};
    const modeCandidates = [
        profileSettings?.mode,
        profileSettings?.profile_mode,
        profileSettings?.broker_mode,
        execution?.mode,
        execution?.profileMode,
        execution?.brokerMode
    ]
        .map((value) => String(value || '').trim().toLowerCase())
        .filter(Boolean);
    if (modeCandidates.some((value) => value === 'omnibus')) {
        return true;
    }

    const flagCandidates = [
        profileSettings?.is_omnibus,
        profileSettings?.is_omnibus_profile,
        profileSettings?.isOmnibus,
        profileSettings?.isOmnibusProfile,
        execution?.is_omnibus,
        execution?.is_omnibus_profile,
        execution?.isOmnibus,
        execution?.isOmnibusProfile
    ];
    return flagCandidates.some((value) => isTruthyFlag(value));
};

const parseBytelystSubTag = (subTagRaw: string): ParsedSubTag | null => {
    const raw = String(subTagRaw || '').trim();
    if (!raw) return null;

    const parts = raw.split(':').map((part) => part.trim());
    if (parts.length < 4) return null;
    if (parts[0].toUpperCase() !== SUBTAG_PREFIX) return null;

    if (parts.length >= 5 && /^P[A-F0-9]+$/i.test(parts[2]) && /^T[A-F0-9]+$/i.test(parts[3])) {
        return {
            format: 'compact',
            raw,
            env: cleanToken(parts[1], 16),
            profileToken: parts[2].toUpperCase(),
            tradeToken: parts[3].toUpperCase(),
            intent: cleanToken(parts[4], 8)
        };
    }

    return {
        format: 'legacy',
        raw,
        env: cleanToken(parts[1], 16),
        profileSegment: cleanToken(parts[2], 64),
        tradeSegment: cleanToken(parts[3], 128)
    };
};

export const isBytelystSubTag = (subTagRaw: string): boolean => {
    const parsed = parseBytelystSubTag(subTagRaw);
    return !!parsed;
};

export const extractOrderSubTag = (order: any): string => {
    return String(order?.subtag || order?.sub_tag || order?.subTag || '').trim();
};

export const shouldAttachAlpacaSubTag = (args: {
    profileId?: string;
    profileSettings?: any;
}): boolean => {
    if (!config.ENABLE_ALPACA_SUBTAG) return false;
    const disabledTargets = parseDisabledExecutionExchanges();
    const executionKeys = resolveExecutionExchangeKeys();
    if (executionKeys.some((key) => disabledTargets.has(key))) return false;
    if (String(config.EXECUTION_PROVIDER || '').trim().toLowerCase() !== 'alpaca') return false;
    if (!config.SUBTAG_OMNIBUS_ONLY) return true;

    const profileId = normalizeProfileId(args.profileId);
    if (!profileId) return false;
    if (resolveOmnibusFromProfile(args.profileSettings)) return true;

    return parseAllowlist().has(profileId);
};

export const buildAlpacaSubTag = (input: BuildAlpacaSubTagInput): string | null => {
    const profileId = normalizeProfileId(input.profileId);
    if (!profileId) return null;

    const envToken = resolveEnvToken(input.envLabel);
    const intentToken = cleanToken(input.intent || 'UNKNOWN', 6) || 'UNKNOWN';
    const profileToken = `P${hashToken(profileId, 10)}`;
    const tradeSeed = String(input.tradeId || input.intent || 'UNKNOWN').trim();
    const tradeToken = `T${hashToken(tradeSeed, 12)}`;

    const value = `${SUBTAG_PREFIX}:${envToken}:${profileToken}:${tradeToken}:${intentToken}`;
    const maxLength = resolveMaxLength(input.maxLength);
    return value.length > maxLength ? value.slice(0, maxLength) : value;
};

export const subTagBelongsToProfile = (subTagRaw: string, profileId: string): boolean => {
    const parsed = parseBytelystSubTag(subTagRaw);
    if (!parsed) return false;

    const normalizedProfileId = normalizeProfileId(profileId);
    if (!normalizedProfileId) return false;

    if (parsed.format === 'compact') {
        return parsed.profileToken === `P${hashToken(normalizedProfileId, 10)}`;
    }

    const profileToken = cleanToken(normalizedProfileId, 64);
    return parsed.profileSegment === profileToken;
};

export const subTagHintsTrade = (subTagRaw: string, tradeId: string): boolean => {
    const parsed = parseBytelystSubTag(subTagRaw);
    if (!parsed) return false;

    const normalizedTradeId = String(tradeId || '').trim();
    if (!normalizedTradeId) return false;

    if (parsed.format === 'compact') {
        return parsed.tradeToken === `T${hashToken(normalizedTradeId, 12)}`;
    }

    const tradeToken = cleanToken(normalizedTradeId, 128);
    return parsed.tradeSegment === tradeToken;
};
