import { DateTime } from 'luxon';

// Market configuration types
interface MarketTimes {
    open: { hour: number; minute: number };
    close: { hour: number; minute: number };
    preMarketOpen: { hour: number; minute: number };
    afterHoursClose: { hour: number; minute: number };
    halfDayClose?: { hour: number; minute: number };
}

interface MarketConfig extends MarketTimes {
    timezone: string;
    holidays?: Date[];
    halfDays?: Date[];
}

// Market hours configuration
export const MarketHours: Record<string, MarketConfig> = {
    US: {
        timezone: 'America/New_York',
        open: { hour: 9, minute: 30 },
        close: { hour: 16, minute: 0 },
        preMarketOpen: { hour: 4, minute: 0 },
        afterHoursClose: { hour: 20, minute: 0 },
        halfDayClose: { hour: 13, minute: 0 },
        holidays: [],
        halfDays: []
    },
    EUROPE: {
        timezone: 'Europe/London',
        open: { hour: 8, minute: 0 },
        close: { hour: 16, minute: 30 },
        preMarketOpen: { hour: 7, minute: 0 },
        afterHoursClose: { hour: 17, minute: 30 },
        holidays: [],
        halfDays: []
    },
    ASIA: {
        timezone: 'Asia/Tokyo',
        open: { hour: 9, minute: 0 },
        close: { hour: 15, minute: 30 },
        preMarketOpen: { hour: 8, minute: 0 },
        afterHoursClose: { hour: 16, minute: 30 },
        holidays: [],
        halfDays: []
    }
} as const;

// Type definitions
export type MarketSession = keyof typeof MarketHours;
export type MarketHoursType = 'regular' | 'preMarket' | 'afterHours';

interface TimeRange {
    start: DateTime;
    end: DateTime;
}

interface MarketStatus {
    isOpen: boolean;
    nextOpen?: Date;
    nextClose?: Date;
    currentSession?: MarketHoursType;
    isHoliday?: boolean;
    isHalfDay?: boolean;
}

export class MarketTimeUtils {
    /**
     * Convert a date to market timezone
     */
    private static toMarketTime(date: Date, market: MarketSession): DateTime {
        return DateTime.fromJSDate(date).setZone(MarketHours[market].timezone, { keepLocalTime: false });
    }

    /**
     * Check if a date is a market holiday
     */
    private static isHoliday(date: Date, market: MarketSession): boolean {
        const marketConfig = MarketHours[market];
        if (!marketConfig.holidays?.length) return false;

        const marketTime = this.toMarketTime(date, market);
        return marketConfig.holidays.some(holiday =>
            marketTime.hasSame(DateTime.fromJSDate(holiday), 'day')
        );
    }

    /**
     * Check if a date is a half day
     */
    private static isHalfDay(date: Date, market: MarketSession): boolean {
        const marketConfig = MarketHours[market];
        if (!marketConfig.halfDays?.length) return false;

        const marketTime = this.toMarketTime(date, market);
        return marketConfig.halfDays.some(halfDay =>
            marketTime.hasSame(DateTime.fromJSDate(halfDay), 'day')
        );
    }

    /**
     * Get market session times for a specific date
     */
    private static getSessionTimes(date: Date, market: MarketSession): {
        preMarketOpen: DateTime;
        regularOpen: DateTime;
        regularClose: DateTime;
        afterHoursClose: DateTime;
    } {
        const marketConfig = MarketHours[market];
        const baseDate = this.toMarketTime(date, market).startOf('day');

        const regularClose = this.isHalfDay(date, market) && marketConfig.halfDayClose
            ? baseDate.set(marketConfig.halfDayClose)
            : baseDate.set(marketConfig.close);

        return {
            preMarketOpen: baseDate.set(marketConfig.preMarketOpen),
            regularOpen: baseDate.set(marketConfig.open),
            regularClose,
            afterHoursClose: baseDate.set(marketConfig.afterHoursClose)
        };
    }

    /**
     * Check if a given date is in a specific market session type
     */
    static isInMarketSession(date: Date, market: MarketSession, type: MarketHoursType = 'regular'): boolean {
        const marketConfig = MarketHours[market];

        // Convert input date to market timezone
        const localMarketTime = DateTime.fromJSDate(date)
            .setZone(marketConfig.timezone, { keepLocalTime: false });

        // Log for debugging
        console.log(`\nMarket Session Check:
            Market: ${market}
            Type: ${type}
            Input UTC: ${date.toISOString()}
            Local Market Time: ${localMarketTime.toString()}
            Day of Week: ${localMarketTime.weekday}`);

        // Weekend check
        if (localMarketTime.weekday > 5) {
            console.log('Weekend detected - Market Closed');
            return false;
        }

        // Get session times in UTC
        const marketDate = localMarketTime.startOf('day');
        const sessionTimes = {
            preMarket: marketDate.set(marketConfig.preMarketOpen).toUTC(),
            open: marketDate.set(marketConfig.open).toUTC(),
            close: marketDate.set(marketConfig.close).toUTC(),
            afterHours: marketDate.set(marketConfig.afterHoursClose).toUTC()
        };

        // Current time in UTC for comparison
        const currentUTC = localMarketTime.toUTC();

        console.log(`Session Times (UTC):
            Pre-Market Start: ${sessionTimes.preMarket.toISO()}
            Market Open: ${sessionTimes.open.toISO()}
            Market Close: ${sessionTimes.close.toISO()}
            After-Hours Close: ${sessionTimes.afterHours.toISO()}
            Current Time: ${currentUTC.toISO()}`);

        // Check appropriate session type
        switch (type) {
            case 'regular':
                // Regular session: >= market open AND < market close
                const isRegularSession = currentUTC >= sessionTimes.open && currentUTC < sessionTimes.close;
                console.log(`Regular Session Check: ${isRegularSession}`);
                return isRegularSession;

            case 'preMarket':
                // Pre-market: >= pre-market start AND < market open
                const isPreMarket = currentUTC >= sessionTimes.preMarket && currentUTC < sessionTimes.open;
                console.log(`Pre-Market Session Check: ${isPreMarket}`);
                return isPreMarket;

            case 'afterHours':
                // After-hours: >= market close AND <= after-hours close
                const isAfterHours = currentUTC >= sessionTimes.close && currentUTC <= sessionTimes.afterHours;
                console.log(`After-Hours Session Check: ${isAfterHours}`);
                return isAfterHours;

            default:
                console.log('Invalid session type');
                return false;
        }
    }

    /**
     * Get all active market sessions for a given time
     */
    static getActiveMarketSessions(date: Date, type: MarketHoursType = 'regular'): MarketSession[] {
        return (Object.keys(MarketHours) as MarketSession[])
            .filter(market => this.isInMarketSession(date, market, type));
    }

    /**
     * Get detailed market status including next open/close times
     */
    static getMarketStatus(date: Date, market: MarketSession): MarketStatus {
        const marketTime = this.toMarketTime(date, market);
        const status: MarketStatus = {
            isOpen: false,
            isHoliday: this.isHoliday(date, market),
            isHalfDay: this.isHalfDay(date, market)
        };

        // Check each session type
        if (this.isInMarketSession(date, market, 'regular')) {
            status.isOpen = true;
            status.currentSession = 'regular';
            status.nextClose = this.getSessionTimes(date, market).regularClose.toJSDate();
        } else if (this.isInMarketSession(date, market, 'preMarket')) {
            status.isOpen = true;
            status.currentSession = 'preMarket';
            status.nextClose = this.getSessionTimes(date, market).regularOpen.toJSDate();
        } else if (this.isInMarketSession(date, market, 'afterHours')) {
            status.isOpen = true;
            status.currentSession = 'afterHours';
            status.nextClose = this.getSessionTimes(date, market).afterHoursClose.toJSDate();
        }

        // Get next open if market is closed
        if (!status.isOpen) {
            status.nextOpen = this.getNextMarketOpen(date, market);
        }

        return status;
    }

    /**
     * Get next market open from a given date
     */
    static getNextMarketOpen(date: Date, market: MarketSession): Date {
        let checkDate = this.toMarketTime(date, market);
        const times = this.getSessionTimes(date, market);

        // If we're before today's open and it's a trading day, return today's open
        if (checkDate < times.regularOpen &&
            checkDate.weekday <= 5 &&
            !this.isHoliday(date, market)) {
            return times.regularOpen.toJSDate();
        }

        // Find next trading day
        do {
            checkDate = checkDate.plus({ days: 1 }).startOf('day');
        } while (
            checkDate.weekday > 5 ||
            this.isHoliday(checkDate.toJSDate(), market)
        );

        return this.getSessionTimes(checkDate.toJSDate(), market).regularOpen.toJSDate();
    }

    /**
     * Calculate trading days between two dates
     */
    static getTradingDaysBetween(startDate: Date, endDate: Date, market: MarketSession): number {
        let current = this.toMarketTime(startDate, market).startOf('day');
        const end = this.toMarketTime(endDate, market).startOf('day');
        let tradingDays = 0;

        // Handle same-day case
        if (current.hasSame(end, 'day')) {
            return (current.weekday <= 5 && !this.isHoliday(current.toJSDate(), market)) ? 1 : 0;
        }

        // Count trading days
        while (current <= end) {
            if (current.weekday <= 5 && !this.isHoliday(current.toJSDate(), market)) {
                tradingDays++;
            }
            current = current.plus({ days: 1 });
        }

        return tradingDays;
    }

    /**
     * Get trading hours for a specific date
     */
    static getTradingHours(date: Date, market: MarketSession): TimeRange {
        const times = this.getSessionTimes(date, market);
        return {
            start: times.regularOpen,
            end: times.regularClose
        };
    }

    /**
     * Update market calendar
     */
    static updateMarketCalendar(
        market: MarketSession,
        holidays?: Date[],
        halfDays?: Date[]
    ): void {
        if (holidays) {
            MarketHours[market].holidays = holidays;
        }
        if (halfDays) {
            MarketHours[market].halfDays = halfDays;
        }
    }

    /**
 * Get previous market close from a given date
 */
    static getPreviousMarketClose(date: Date, market: MarketSession): Date {
        console.log('\nFinding previous market close:');
        let checkDate = this.toMarketTime(date, market);
        const marketConfig = MarketHours[market];

        console.log('Input date in market timezone:', checkDate.toString());

        // If it's weekend, move back to Friday
        while (checkDate.weekday > 5) {
            checkDate = checkDate.minus({ days: 1 });
            console.log('Moving back to previous day:', checkDate.toString());
        }

        // Set to market close time
        const closeTime = checkDate.set(marketConfig.close);
        console.log('Close time for check date:', closeTime.toString());

        // If we're before today's close, move to previous business day
        if (checkDate.toMillis() < closeTime.toMillis()) {
            console.log('Before today\'s close, moving to previous business day');
            do {
                checkDate = checkDate.minus({ days: 1 });
                console.log('Checking date:', checkDate.toString());
            } while (checkDate.weekday > 5);

            return checkDate.set(marketConfig.close).toJSDate();
        }

        return closeTime.toJSDate();
    }
}

i have this code

and i want to debug it in typescript, postgresql next.js etc...

i gave it some tests nmow can you make some more? it's for a day trading journaling app, make some hard tests!!! return full code

here is my current test:import { DateTime } from 'luxon';
import { MarketTimeUtils, MarketSession, MarketHours } from '@/app/lib/market-time';

describe('MarketTimeUtils Market Close Transition Tests', () => {
    const createTestTime = (hour: number, minute: number, second: number = 0) => {
        return DateTime.fromObject(
            {
                year: 2024,
                month: 3,
                day: 15,  // Friday
                hour,
                minute,
                second
            },
            { zone: MarketHours.US.timezone }
        ).toJSDate();
    };

    describe('Market Close Boundary Tests', () => {
        it('should test exact market close times with detailed logging', () => {
            const times = [
                { time: createTestTime(15, 59, 59), session: 'regular' },
                { time: createTestTime(16, 0, 0), session: 'afterHours' },
                { time: createTestTime(16, 0, 1), session: 'afterHours' }
            ];

            times.forEach(({ time, session }) => {
                console.log('\n=== Test Case ===');
                console.log('Test time:', DateTime.fromJSDate(time).toISO());
                console.log('Expected session:', session);

                const status = MarketTimeUtils.getMarketStatus(time, 'US');
                const regularSession = MarketTimeUtils.isInMarketSession(time, 'US', 'regular');
                const afterHours = MarketTimeUtils.isInMarketSession(time, 'US', 'afterHours');

                console.log('Market Status:', {
                    isOpen: status.isOpen,
                    currentSession: status.currentSession,
                    regularSession,
                    afterHours
                });

                expect(status.currentSession).toBe(session);
            });
        });

        it('should properly detect after-hours at exactly market close time', () => {
            const marketClose = createTestTime(16, 0, 0);

            console.log('\n=== Market Close Test ===');
            console.log('Market Close Time:', DateTime.fromJSDate(marketClose).toISO());

            const regularSession = MarketTimeUtils.isInMarketSession(marketClose, 'US', 'regular');
            const afterHours = MarketTimeUtils.isInMarketSession(marketClose, 'US', 'afterHours');
            const status = MarketTimeUtils.getMarketStatus(marketClose, 'US');

            console.log('Session Detection:', {
                regularSession,
                afterHours,
                status
            });

            expect(regularSession).toBe(false);
            expect(afterHours).toBe(true);
            expect(status.currentSession).toBe('afterHours');
        });

        // Test microsecond precision around market close
        it('should handle microsecond-precise market close transition', () => {
            const beforeClose = DateTime.fromObject(
                { year: 2024, month: 3, day: 15, hour: 16, minute: 0, millisecond: -1 },
                { zone: MarketHours.US.timezone }
            ).toJSDate();

            const atClose = DateTime.fromObject(
                { year: 2024, month: 3, day: 15, hour: 16, minute: 0, millisecond: 0 },
                { zone: MarketHours.US.timezone }
            ).toJSDate();

            console.log('\n=== Microsecond Precision Test ===');
            console.log('Just before close:', DateTime.fromJSDate(beforeClose).toISO());
            console.log('At close:', DateTime.fromJSDate(atClose).toISO());

            const beforeStatus = MarketTimeUtils.getMarketStatus(beforeClose, 'US');
            const atCloseStatus = MarketTimeUtils.getMarketStatus(atClose, 'US');

            console.log('Status just before:', beforeStatus);
            console.log('Status at close:', atCloseStatus);

            expect(beforeStatus.currentSession).toBe('regular');
            expect(atCloseStatus.currentSession).toBe('afterHours');
        });
    });
});