// #region Constants

declare var chrome: any;

interface AppSettings {
    eventId: number;
    quantity: number;
    searchType: "Regular" | "Resale";
    minPrice: number;
    maxPrice: number;
    interval: number;
    sound: string;
    errorSound: string;
    startSound: string;
}

const DEFAULT_CONTENT_SETTINGS: AppSettings = {
    eventId: 3730,
    quantity: 1,
    searchType: "Resale",
    minPrice: 0,
    maxPrice: 6000,
    interval: 7000,
    sound: "none",
    errorSound: "none",
    startSound: "none"
};

// #endregion Constants


// #region Interfaces / Types

type SearchTypes = "Regular" | "Resale";
enum SearchTypesEnum {
    Regular = "Regular",
    Resale = "Resale",
}

interface AvaliableSeatResponse {
    AreaId: number;
    PriceBands: PriceBand[];
}

interface PriceBand {

    PriceBandCode: number,
    MinimalBayerAmount?: number,
    MinimalBayerAmountFormatted?: string,
    Fee?: number,
    FeeFormatted?: string,
    TotalValueFormatted?: string,
    HasFee?: boolean,
    AvailableSeatsInterval: AvailableSeatsInterval[]
}

interface AvailableSeatsInterval {
    YCoord: number;
    StartXCoord: number;
    EndXCoord: number;
}




interface PostLockSeatsResponse {
    LockedSeats: PostLockedSeat[];
    PriceBandId: number;
    LockingTime: string;

}

interface PostLockedSeatPrices {

    PriceCategory: number,
    PriceCategoryDescription: string,
    PriceType: number,
    PriceTypeDescription: string,
    Price: number,
    PriceFormatted: string,
    TicketFeeFormatted: string,
    TotalFaceValue: number,
    TotalFaceValueFormatted: string,
    IsSeatFee: boolean,
    BlockingCodes: any,
    Restriction: any,
    FaceValueFormatted: any

}

interface PostLockedSeat {
    Prices: PostLockedSeatPrices[];
    Id: number;
    AreaId: number;
    XCoord: number;
    YCoord: number;
    RowName: string;
}

interface PostLockSeatsPayload {
    AreSeatsTogether: boolean;
    IsGeneralAdmissionEnabled: boolean;
    AreaId: number;
    EventId: number;
    MinimumPrice: number;
    MaximumPrice: number;
    PriceBandId: number;
    Quantity: number;
    SeatAttributeIds: any[];
}


interface PutLockSeatsPayload {
    EventId: number,
    Seats: PutLockSeatsSeats[]
}
interface PutLockSeatsSeats {
    Id: number,
    PriceClassId: number,
}



interface EventIdPayload {
    eventId: number;
}


interface BasketSeatsResponse {
    XCoord: number,
    YCoord: number,
    AreaId: string,
    PriceClassId: number,
    PriceBandId: number,
    SeatType: number,
    AdditionalData: any
}


interface SelectResaleSeatPayload {
    EventId: number,
    Seats: SelectResaleSeatSeats[]
}

interface SelectResaleSeatSeats {
    AreaId: number,
    BlockId: number,
    IsSecodaryMarket: boolean,
    PriceClassId: number,
    XCoordinate: number,
    YCoordinate: number,
}


interface RequestResult<T> {
    status: number;
    isSuccess: boolean;
    body: T;
}



// #region Factory

class TicketmasterRequestsFactory {

    create(gameId: number, searchType: SearchTypes, quantity: number, searchIndex: number = 0): ITicketmasterRequests {
        switch (searchType) {
            case SearchTypesEnum.Regular:
                return new SearchListTickets(gameId, searchType, quantity, searchIndex);
                break;
            case SearchTypesEnum.Resale:
                return new SearchListTickets(gameId, searchType, quantity, searchIndex);
                break;
            default:
                return new SearchListTickets(gameId, searchType, quantity, searchIndex);
                break;
        }
    }
}




// #endregion Factory

const getHeaders = (gameId: number): HeadersInit => {
    return {
        'accept': 'application/json, text/plain, */*',
        'accept-language': 'de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7',
        'cookie': document.cookie,
        'priority': 'u=1, i',
        'referer': gameId === 0
            ? 'https://www.eticketing.co.uk/arsenal/Events'
            : `https://www.eticketing.co.uk/arsenal/EDP/Event/Index/${gameId}?position=1`,
        'sec-ch-ua': '"Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"macOS"',
        'sec-fetch-dest': 'empty',
        'sec-fetch-mode': 'cors',
        'sec-fetch-site': 'same-origin',
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
        'x-requested-with': 'XMLHttpRequest',
        'content-type': "application/json",
    }
}

const handleRequest = async <T, B>(gameId: number, url: string, method: string, body?: B, responseJson: boolean = true): Promise<RequestResult<T>> => {
    let options: RequestInit = {
        method: method,
        headers: getHeaders(gameId)
    };
    if (body) options.body = JSON.stringify(body);
    let response: Response | null = null;
    try {
        response = await fetch(url, options);
    } catch (error) {
        console.log('Error handling the request. Trying again.');
        console.log(error);
    }
    if (response === null) return { isSuccess: false, status: -1, body: null as T }
    let responseBody: T = null as T;
    try { responseBody = (responseJson) ? await response.json() : null as T } catch (error) { responseBody = null as T; }


    let resultResponse: RequestResult<T> = {
        status: response.status,
        isSuccess: (response.status === 200) ? true : false,
        body: responseBody,
    }
    return resultResponse;
}

const deletePurgLocks = async (gameId: number): Promise<boolean> => {
    let url = `https://www.eticketing.co.uk/arsenal/EDP/BestAvailable/PurgeLocks`;
    let response = await handleRequest<null, EventIdPayload>(gameId, url, 'DELETE', { eventId: gameId }, false);
    return response.isSuccess;
}

const basketSeats = async (gameId: number): Promise<boolean> => {
    let url = `https://www.eticketing.co.uk/arsenal/EDP/Ism/BasketSeats`;
    let response = await handleRequest<BasketSeatsResponse[], EventIdPayload>(gameId, url, 'GET', { eventId: gameId }, false);
    return response.isSuccess;
}




interface ITicketmasterRequests {

    gameId: number;
    quantity: number;
    seatsTogehther: boolean;
    searchType: SearchTypes;


    handle: () => Promise<boolean>;



}



class SearchListTickets implements ITicketmasterRequests {


    gameId: number;
    quantity: number;

    seatsTogehther: boolean;
    searchType: SearchTypes;

    searchUrl: string;
    lockTicketUrl: string;
    searchIndex: number;

    minPrice: number;
    maxPrice: number;



    constructor(gameId: number, searchType: SearchTypes, quantity: number, searchIndex: number, minPrice: number = 0, maxPrice: number = 6000) {
        this.gameId = gameId;
        this.searchType = searchType;
        this.quantity = quantity;
        this.seatsTogehther = (quantity > 1) ? true : false;
        this.searchIndex = searchIndex;
        this.minPrice = minPrice;
        this.maxPrice = maxPrice;

        this.searchUrl = `https://www.eticketing.co.uk/arsenal/EDP/Seats/Available${this.searchType}?AreSeatsTogether=${this.seatsTogehther}&EventId=${this.gameId}&MarketType=1&MaximumPrice=${this.maxPrice}&MinimumPrice=0&Quantity=${this.quantity}`;
        this.lockTicketUrl = `https://www.eticketing.co.uk/arsenal/EDP/BestAvailable/${this.searchType}Seats`;

    }

    async handle(): Promise<boolean> {
        let searchResponse = await handleRequest<AvaliableSeatResponse[], undefined>(this.gameId, this.searchUrl, 'GET');

        // If search fails (but not just 404 'no tickets'), throw to trigger error sound
        if (!searchResponse.isSuccess && searchResponse.status !== 404 && searchResponse.status !== -1) {
            throw new Error(`Search request failed with status ${searchResponse.status}`);
        }

        if (searchResponse.body && searchResponse.body.length > 0) {

            let cheapestTicket: AvaliableSeatResponse = searchResponse.body[this.searchIndex]!;
            for (let i = 0; i < searchResponse.body.length; i++) {
                if (searchResponse.body[i].PriceBands[0].MinimalBayerAmount! < cheapestTicket.PriceBands[0].MinimalBayerAmount!) {
                    cheapestTicket = searchResponse.body[i];
                }
            }

            // //console.log(cheapestTicket.PriceBands[0]?.MinimalBayerAmount!);
            if (cheapestTicket.PriceBands[0]?.MinimalBayerAmount! < this.minPrice ||
                cheapestTicket.PriceBands[0]?.MinimalBayerAmount! > this.maxPrice) {
                console.log('not in price range');
                return false;
            }



            let postLockTicketResponse = await handleRequest<PostLockSeatsResponse, PostLockSeatsPayload>(this.gameId, this.lockTicketUrl, 'POST', {
                EventId: this.gameId,
                Quantity: this.quantity,
                AreSeatsTogether: this.seatsTogehther,
                AreaId: cheapestTicket.AreaId,
                PriceBandId: cheapestTicket.PriceBands[0].PriceBandCode,
                IsGeneralAdmissionEnabled: false,
                SeatAttributeIds: [],
                MinimumPrice: 0,
                MaximumPrice: 10000000
            });

            if (!postLockTicketResponse.isSuccess) {
                throw new Error(`Lock request (POST) failed with status ${postLockTicketResponse.status}`);
            }





            let putLockTicketResponse = await handleRequest<PostLockSeatsResponse, PutLockSeatsPayload>(this.gameId, this.lockTicketUrl, 'PUT', {
                EventId: this.gameId,
                Seats: postLockTicketResponse.body.LockedSeats.map(seat => {
                    return {
                        Id: seat.Id,
                        PriceClassId: 1

                        // PriceClassId: seat.Prices[0]?.PriceType ?? 1
                    }
                })
            });

            if (!putLockTicketResponse.isSuccess) {
                throw new Error(`Lock request (PUT) failed with status ${putLockTicketResponse.status}`);
            }
            return true;

        }

        return false;

    }

}



// #endregion Interfaces


// #region TicketMasterRequests Class


// class TicketmasterRequests {


//     private gameId: number = 0;
//     private searchType: SearchTypes;
//     private quantity: number = 1;
//     private seatsTogehther: boolean = false;

//     constructor(gameId: number, searchType: SearchTypes, quantity: number) {
//         this.gameId = gameId;
//         this.searchType = searchType;
//         this.quantity = quantity;
//         this.seatsTogehther = (quantity > 1) ? true : false;

//     }

//     async handleRequest<T, B>(url: string, method: string, body?: B, responseJson: boolean = true): Promise<RequestResult<T>> {
//         let options: RequestInit = {
//             method: method,
//             headers: this.getHeaders()
//         };
//         if (body) options.body = JSON.stringify(body);
//         let response = await fetch(url, options);

//         let resultResponse : RequestResult<T> = {
//             status: response.status,
//             isSuccess: this.checkStatus(response.status),
//             body: (responseJson) ? await response.json() as T : null as T
//         }
//         return resultResponse;
//     }

//     checkStatus(statusCode: number) : boolean {
//         switch (statusCode) {
//             case 200:
//                 return true;
//             case 401:
//                 return false;
//             case 403:
//                 return false;
//             default:
//                 return false;
//         }
//     }



//     async getCheckForRegularTicketsRequest(): Promise<AvaliableSeatResponse[]> {
//         let url = `https://www.eticketing.co.uk/arsenal/EDP/Seats/AvailableRegular?AreSeatsTogether=${ this.seatsTogehther }&EventId=${this.gameId}&MaximumPrice=10000000&MinimumPrice=0&Quantity=${this.quantity}`;

//         let response = await this.handleRequest<AvaliableSeatResponse[], undefined>(url, 'GET');
//         return response.body;



//     }

//     async getCheckForResaleTicketsRequest():Promise<AvaliableSeatResponse[]> {
//         let url = `https://www.eticketing.co.uk/arsenal/EDP/Seats/AvailableResale?AreSeatsTogether=${this.seatsTogehther}&EventId=${this.gameId}&MarketType=1&MaximumPrice=10000000&MinimumPrice=0&Quantity=${this.quantity}`;

//         let response = await this.handleRequest<AvaliableSeatResponse[], undefined>(url, 'GET');
//         return response.body;
//     }

//     async handleTicketFound(avaliableSeatResponse: AvaliableSeatResponse) : Promise<boolean> {
//         let postLockTicket = await this.postLockTicketsRequest({
//             EventId: this.gameId,
//             Quantity: this.quantity,
//             AreSeatsTogether: this.seatsTogehther,
//             AreaId: avaliableSeatResponse.AreaId,
//             PriceBandId: avaliableSeatResponse.PriceBands[0].PriceBandCode,
//             SeatAttributeIds: [],
//             MinimumPrice: 0,
//             MaximumPrice: 10000000
//         });

//         if (!postLockTicket.isSuccess) return false;

//         let putLockTicket = await this.putLockTicketsRequest({
//             EventId: this.gameId,
//             Seats: postLockTicket.body.LockedSeats.map(seat => {
//                 return {
//                     Id: seat.Id,
//                     PriceClassId: 1
//                 }
//             })
//         });

//         if (!putLockTicket.isSuccess) return false;

//         let basketSeats = await this.getBasketSeatsRequest();

//         if (!basketSeats.isSuccess) return false;

//         return true
//     }



//     async deletePurgeLocksRequest(): Promise<void> {
//         let url = `https://www.eticketing.co.uk/arsenal/EDP/BestAvailable/PurgeLocks`;
//         let response = await this.handleRequest<null, EventIdPayload>(url, 'DELETE', {eventId: this.gameId}, false);
//     }

//     getLockSeatUrl(): string {

//         let resaleUrl = `https://www.eticketing.co.uk/arsenal/EDP/BestAvailable/ResaleSeats`;
//         let regularUrl = `https://www.eticketing.co.uk/arsenal/EDP/BestAvailable/RegularSeats`;
//         switch(this.searchType) {
//             case SearchTypesEnum.Regular:
//                 return regularUrl
//                 break;
//             case SearchTypesEnum.Resale:
//                 return resaleUrl;
//                 break;
//             default:
//                 return resaleUrl;
//                 break;
//         }
//     }


//     async postLockTicketsRequest(lockSeatsPayload: PostLockSeatsPayload): Promise<RequestResult<PostLockSeatsResponse>> {

//         //let url = (this.searchType === this.searchType.)`https://www.eticketing.co.uk/arsenal/EDP/BestAvailable/RegularSeats`;
//         let url = this.getLockSeatUrl();
//         let response = await this.handleRequest<PostLockSeatsResponse, PostLockSeatsPayload>(url, 'POST', lockSeatsPayload);
//         return response;
//     }
//     async putLockTicketsRequest(lockSeatsPayload: PutLockSeatsPayload): Promise<RequestResult<PostLockSeatsResponse>> {
//         let url = this.getLockSeatUrl();
//         let response = await this.handleRequest<PostLockSeatsResponse, PutLockSeatsPayload>(url, 'PUT', lockSeatsPayload);
//         return response;
//     }


//     async getBasketSeatsRequest(): Promise<RequestResult<BasketSeatsResponse[]>> {
//         let url = `https://www.eticketing.co.uk/arsenal/EDP/Ism/BasketSeats`;
//         let response = await this.handleRequest<BasketSeatsResponse[], EventIdPayload>(url, 'GET', {eventId: this.gameId});
//         return response;
//     }


// }

// #endregion TicketMasterRequests Class


// #region Main

console.log('Okaaaay, lets go');

// Aggressive helper to play audio with fallback
const playAudio = (soundUrl: string, logName: string) => {
    const attemptPlay = (isRetry = false) => {
        const audio = new Audio(soundUrl);
        console.log(`🎵 [${isRetry ? 'Retry' : 'Initial'}] Playing ${logName}...`);

        audio.play().then(() => {
            console.log(`✅ success: ${logName} played.`);
        }).catch(e => {
            if (!isRetry) {
                console.warn(`⚠️ Autoplay blocked for ${logName}. Bitte klicke einmal irgendwo auf die Arsenal-Webseite (nicht das Popup!), um den Ton zu aktivieren.`);

                const onInteraction = () => {
                    console.log('🖱️ Interaktion erkannt! Versuche Ton erneut abzuspielen...');
                    attemptPlay(true);
                    window.removeEventListener('click', onInteraction, true);
                    window.removeEventListener('keydown', onInteraction, true);
                };

                // Use capture: true to ensure we see the event even if the page tries to stop it
                window.addEventListener('click', onInteraction, true);
                window.addEventListener('keydown', onInteraction, true);
            } else {
                console.error(`❌ Playback failed even after interaction for ${logName}:`, e);
            }
        });
    };
    attemptPlay();
};


const run = async (settings: AppSettings) => {
    console.log('Starting with settings:', settings);

    // Play start sound
    if (settings.startSound && settings.startSound !== 'none') {
        const soundUrl = chrome.runtime.getURL(`sounds/${settings.startSound}`);
        playAudio(soundUrl, `Start Sound (${settings.startSound})`);
    }

    // Search index is not in settings yet, default to 0
    const SEARCH_INDEX = 0;

    let ticketHandler = new TicketmasterRequestsFactory().create(
        settings.eventId,
        settings.searchType,
        settings.quantity,
        SEARCH_INDEX
    );

    // Re-instantiate with full params
    ticketHandler = new SearchListTickets(
        settings.eventId,
        settings.searchType,
        settings.quantity,
        SEARCH_INDEX,
        settings.minPrice,
        settings.maxPrice
    );


    await deletePurgLocks(settings.eventId);
    let ticketFound = false;
    while (ticketFound === false) {
        try {
            let result = await ticketHandler.handle();
            if (result) {
                await basketSeats(settings.eventId);
                console.log('Ticket in basket');

                // Play success sound
                if (settings.sound && settings.sound !== 'none') {
                    const soundUrl = chrome.runtime.getURL(`sounds/${settings.sound}`);
                    playAudio(soundUrl, `Success Sound (${settings.sound})`);
                }

                ticketFound = true;
                break;
            }
        } catch (error) {
            // results of break statement
            console.log('try again')
            console.log(error);

            // Play failure sound on specific errors (e.g., 404 or other request failures)
            // Assuming error object or flow implies failure
            if (settings.errorSound && settings.errorSound !== 'none') {
                const soundUrl = chrome.runtime.getURL(`sounds/${settings.errorSound}`);
                playAudio(soundUrl, `Failure Sound (${settings.errorSound})`);
            }
        }

        let randomInterval = settings.interval + (Math.random() * settings.interval * 0.2) - (Math.random() * settings.interval * 0.2);
        console.log('No ticket found - waiting for ' + randomInterval.toFixed(2) + ' ms');
        await new Promise(resolve => setTimeout(resolve, randomInterval));
    }
}



// #endregion Main

// #endregion Main

// Start logic only if we are on an event page
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
    // Basic check if we are on an event detail page before running the sniper
    if (window.location.href.includes('/EDP/Event/Index/')) {
        console.log('Event page detected, starting sniper...');
        chrome.storage.local.get(['extensionSettings'], (result: any) => {
            // Merge defaults with stored settings to ensure new properties (like startSound) exist
            // even if the user has old settings saved.
            const settings: AppSettings = { ...DEFAULT_CONTENT_SETTINGS, ...result.extensionSettings };
            run(settings);
        });
    } else {
        console.log('Not an event page, sniper idle. Waiting for messages...');
    }
} else {
    // Fallback
    console.log('Chrome storage not available or fallback mode');
}

// #endregion Message Listener





