import 'dotenv/config';
import { config, loadDynamicConfig } from '../src/config/index.js';
import { AlpacaConnector } from '../src/connectors/alpaca.js';
import { TradeExecutor } from '../src/services/TradeExecutor.js';
import { reconciliationOrderCoverageService } from '../src/services/reconciliationOrderCoverageService.js';
import { supabaseService } from '../src/services/SupabaseService.js';

type CliOptions = {
    apply: boolean;
    profileIds: Set<string>;
    lookbackHours?: number;
    fetchLimitPerPage?: number;
    maxFetchPages?: number;
    maxInsertsPerProfile?: number;
    ignoreFeatureFlag: boolean;
};

type ProfileSummary = {
    profileId: string;
    userId: string;
    attempted: boolean;
    skippedReason?: string;
    dryRun: boolean;
    scannedOrders: number;
    filledLikeOrders: number;
    botOwnedOrders: number;
    eligibleOrders: number;
    missingInDb: number;
    insertedRows: number;
    skippedNotBotOwned: number;
    skippedUnmappedTrade: number;
    skippedUnmappedAction: number;
    skippedMissingFillData: number;
    skippedMissingOrderId: number;
    skippedExisting: number;
    skippedMaxInsertLimit: number;
};

const parseOptions = (argv: string[]): CliOptions => {
    const options: CliOptions = {
        apply: false,
        profileIds: new Set<string>(),
        ignoreFeatureFlag: false
    };

    for (const arg of argv) {
        if (arg === '--apply') {
            options.apply = true;
            continue;
        }
        if (arg === '--ignore-feature-flag') {
            options.ignoreFeatureFlag = true;
            continue;
        }
        if (arg.startsWith('--profile=')) {
            const profileId = String(arg.slice('--profile='.length) || '').trim();
            if (profileId) options.profileIds.add(profileId);
            continue;
        }
        if (arg.startsWith('--lookback-hours=')) {
            const parsed = Number(arg.slice('--lookback-hours='.length));
            if (Number.isFinite(parsed) && parsed > 0) options.lookbackHours = Math.floor(parsed);
            continue;
        }
        if (arg.startsWith('--max-inserts-per-profile=')) {
            const parsed = Number(arg.slice('--max-inserts-per-profile='.length));
            if (Number.isFinite(parsed) && parsed > 0) options.maxInsertsPerProfile = Math.floor(parsed);
            continue;
        }
        if (arg.startsWith('--fetch-limit-per-page=')) {
            const parsed = Number(arg.slice('--fetch-limit-per-page='.length));
            if (Number.isFinite(parsed) && parsed > 0) options.fetchLimitPerPage = Math.floor(parsed);
            continue;
        }
        if (arg.startsWith('--max-fetch-pages=')) {
            const parsed = Number(arg.slice('--max-fetch-pages='.length));
            if (Number.isFinite(parsed) && parsed > 0) options.maxFetchPages = Math.floor(parsed);
            continue;
        }
    }

    return options;
};

const isPlaceholder = (value: string | undefined): boolean => {
    const normalized = String(value || '').trim();
    if (!normalized) return true;
    return normalized === 'your_key' || normalized === 'your_secret';
};

const normalizeProfileIds = (profileIds: Set<string>): string[] => {
    return Array.from(profileIds)
        .map((value) => String(value || '').trim())
        .filter(Boolean);
};

const run = async (): Promise<void> => {
    const options = parseOptions(process.argv.slice(2));
    await loadDynamicConfig(supabaseService);

    const originalEnabled = config.ENABLE_RECON_ORDER_COVERAGE_SYNC;
    const originalDryRun = config.RECON_ORDER_COVERAGE_DRY_RUN;
    const originalLookback = config.RECON_ORDER_COVERAGE_LOOKBACK_HOURS;
    const originalFetchLimitPerPage = config.RECON_ORDER_COVERAGE_FETCH_LIMIT_PER_PAGE;
    const originalMaxFetchPages = config.RECON_ORDER_COVERAGE_MAX_FETCH_PAGES;
    const originalMaxInserts = config.RECON_ORDER_COVERAGE_MAX_INSERTS_PER_PROFILE;

    if (!config.ENABLE_RECON_ORDER_COVERAGE_SYNC && !options.ignoreFeatureFlag) {
        throw new Error('ENABLE_RECON_ORDER_COVERAGE_SYNC=false. Enable it or pass --ignore-feature-flag for one-shot run.');
    }

    if (options.ignoreFeatureFlag) {
        config.ENABLE_RECON_ORDER_COVERAGE_SYNC = true;
    }
    config.RECON_ORDER_COVERAGE_DRY_RUN = !options.apply;
    if (Number.isFinite(options.lookbackHours)) {
        config.RECON_ORDER_COVERAGE_LOOKBACK_HOURS = Number(options.lookbackHours);
    }
    if (Number.isFinite(options.fetchLimitPerPage)) {
        config.RECON_ORDER_COVERAGE_FETCH_LIMIT_PER_PAGE = Number(options.fetchLimitPerPage);
    }
    if (Number.isFinite(options.maxFetchPages)) {
        config.RECON_ORDER_COVERAGE_MAX_FETCH_PAGES = Number(options.maxFetchPages);
    }
    if (Number.isFinite(options.maxInsertsPerProfile)) {
        config.RECON_ORDER_COVERAGE_MAX_INSERTS_PER_PROFILE = Number(options.maxInsertsPerProfile);
    }
    const effectiveLookbackHours = config.RECON_ORDER_COVERAGE_LOOKBACK_HOURS;
    const effectiveFetchLimitPerPage = config.RECON_ORDER_COVERAGE_FETCH_LIMIT_PER_PAGE;
    const effectiveMaxFetchPages = config.RECON_ORDER_COVERAGE_MAX_FETCH_PAGES;
    const effectiveMaxInsertsPerProfile = config.RECON_ORDER_COVERAGE_MAX_INSERTS_PER_PROFILE;
    const effectiveDryRunFlag = config.RECON_ORDER_COVERAGE_DRY_RUN;

    const [users, profiles] = await Promise.all([
        supabaseService.getActiveUsers(),
        supabaseService.getActiveProfiles()
    ]);

    const userById = new Map<string, any>();
    for (const user of users || []) {
        const userId = String((user as any)?.user_id || '').trim();
        if (!userId) continue;
        userById.set(userId, user);
    }

    const selectedProfiles = (profiles || []).filter((profile: any) => {
        const profileId = String(profile?.id || '').trim();
        if (!profileId) return false;
        if (options.profileIds.size === 0) return true;
        return options.profileIds.has(profileId);
    });

    const results: ProfileSummary[] = [];

    for (const profile of selectedProfiles) {
        const profileId = String(profile?.id || '').trim();
        const userId = String(profile?.user_id || '').trim();
        if (!profileId || !userId) continue;

        const user = userById.get(userId);
        if (!user) {
            results.push({
                profileId,
                userId,
                attempted: false,
                skippedReason: 'user_not_found',
                dryRun: config.RECON_ORDER_COVERAGE_DRY_RUN,
                scannedOrders: 0,
                filledLikeOrders: 0,
                botOwnedOrders: 0,
                eligibleOrders: 0,
                missingInDb: 0,
                insertedRows: 0,
                skippedNotBotOwned: 0,
                skippedUnmappedTrade: 0,
                skippedUnmappedAction: 0,
                skippedMissingFillData: 0,
                skippedMissingOrderId: 0,
                skippedExisting: 0,
                skippedMaxInsertLimit: 0
            });
            continue;
        }

        const apiKey = config.PAPER_TRADING ? user.ALPACA_API_KEY : user.REAL_ALPACA_API_KEY;
        const apiSecret = config.PAPER_TRADING ? user.ALPACA_SECRET_KEY : user.REAL_ALPACA_SECRET_KEY;
        if (isPlaceholder(apiKey) || isPlaceholder(apiSecret)) {
            results.push({
                profileId,
                userId,
                attempted: false,
                skippedReason: 'missing_exchange_credentials',
                dryRun: config.RECON_ORDER_COVERAGE_DRY_RUN,
                scannedOrders: 0,
                filledLikeOrders: 0,
                botOwnedOrders: 0,
                eligibleOrders: 0,
                missingInDb: 0,
                insertedRows: 0,
                skippedNotBotOwned: 0,
                skippedUnmappedTrade: 0,
                skippedUnmappedAction: 0,
                skippedMissingFillData: 0,
                skippedMissingOrderId: 0,
                skippedExisting: 0,
                skippedMaxInsertLimit: 0
            });
            continue;
        }

        const connector = new AlpacaConnector(apiKey, apiSecret);
        const executor = new TradeExecutor(connector, undefined, userId, profileId);
        executor.setProfileSettings(profile);

        try {
            const result = await reconciliationOrderCoverageService.runProfile({
                profileId,
                userId,
                executor
            });
            results.push({
                profileId,
                userId,
                attempted: result.attempted,
                skippedReason: result.skippedReason,
                dryRun: result.dryRun,
                scannedOrders: result.scannedOrders,
                filledLikeOrders: result.filledLikeOrders,
                botOwnedOrders: result.botOwnedOrders,
                eligibleOrders: result.eligibleOrders,
                missingInDb: result.missingInDb,
                insertedRows: result.insertedRows,
                skippedNotBotOwned: result.skippedNotBotOwned,
                skippedUnmappedTrade: result.skippedUnmappedTrade,
                skippedUnmappedAction: result.skippedUnmappedAction,
                skippedMissingFillData: result.skippedMissingFillData,
                skippedMissingOrderId: result.skippedMissingOrderId,
                skippedExisting: result.skippedExisting,
                skippedMaxInsertLimit: result.skippedMaxInsertLimit
            });
        } finally {
            executor.dispose();
        }
    }

    config.ENABLE_RECON_ORDER_COVERAGE_SYNC = originalEnabled;
    config.RECON_ORDER_COVERAGE_DRY_RUN = originalDryRun;
    config.RECON_ORDER_COVERAGE_LOOKBACK_HOURS = originalLookback;
    config.RECON_ORDER_COVERAGE_FETCH_LIMIT_PER_PAGE = originalFetchLimitPerPage;
    config.RECON_ORDER_COVERAGE_MAX_FETCH_PAGES = originalMaxFetchPages;
    config.RECON_ORDER_COVERAGE_MAX_INSERTS_PER_PROFILE = originalMaxInserts;

    const aggregate = results.reduce((acc, row) => {
        if (row.attempted) acc.attemptedProfiles += 1;
        if (!row.attempted && row.skippedReason) {
            acc.skippedProfiles[row.skippedReason] = (acc.skippedProfiles[row.skippedReason] || 0) + 1;
        }
        acc.scannedOrders += row.scannedOrders;
        acc.filledLikeOrders += row.filledLikeOrders;
        acc.botOwnedOrders += row.botOwnedOrders;
        acc.eligibleOrders += row.eligibleOrders;
        acc.missingInDb += row.missingInDb;
        acc.insertedRows += row.insertedRows;
        acc.skippedNotBotOwned += row.skippedNotBotOwned;
        acc.skippedUnmappedTrade += row.skippedUnmappedTrade;
        acc.skippedUnmappedAction += row.skippedUnmappedAction;
        acc.skippedMissingFillData += row.skippedMissingFillData;
        acc.skippedMissingOrderId += row.skippedMissingOrderId;
        acc.skippedExisting += row.skippedExisting;
        acc.skippedMaxInsertLimit += row.skippedMaxInsertLimit;
        return acc;
    }, {
        attemptedProfiles: 0,
        skippedProfiles: {} as Record<string, number>,
        scannedOrders: 0,
        filledLikeOrders: 0,
        botOwnedOrders: 0,
        eligibleOrders: 0,
        missingInDb: 0,
        insertedRows: 0,
        skippedNotBotOwned: 0,
        skippedUnmappedTrade: 0,
        skippedUnmappedAction: 0,
        skippedMissingFillData: 0,
        skippedMissingOrderId: 0,
        skippedExisting: 0,
        skippedMaxInsertLimit: 0
    });

    console.log(JSON.stringify({
        mode: options.apply ? 'apply' : 'dry-run',
        profileFilter: normalizeProfileIds(options.profileIds),
        ignoreFeatureFlag: options.ignoreFeatureFlag,
        configuredLookbackHours: effectiveLookbackHours,
        configuredFetchLimitPerPage: effectiveFetchLimitPerPage,
        configuredMaxFetchPages: effectiveMaxFetchPages,
        configuredMaxInsertsPerProfile: effectiveMaxInsertsPerProfile,
        dryRunFlagUsed: effectiveDryRunFlag,
        aggregate,
        results
    }, null, 2));
};

run().catch((error) => {
    const message = error instanceof Error ? error.message : String(error);
    console.error(JSON.stringify({ error: message }, null, 2));
    process.exit(1);
});
