"use strict";

(async () => {
    // This file cannot import config.js or logger.js directly because it runs in a different context (source:https://stackoverflow.com/a/53033388/4235602)
    // So we need to load them dynamically. But our config.js and logger.js are in the same directory and can import each other
    try {
        //#region load logger
        const loggerSrc = chrome.runtime.getURL("logger.js");
        const loggerModule = await import(loggerSrc);
        const logger = loggerModule.default;
        
        logger.debug("Logger initialized successfully");
        logger.debug("Content script starting...");
        //#endregion

        //#region load config
        const configSrc = chrome.runtime.getURL("config.js");
        const configModule = await import(configSrc);
        const config = configModule.default;
        
        logger.debug("Config loaded:", config);
        //#endregion

        class CaptionCapture {
            constructor() {
                this.isCapturing = false;
                this.intervalId = null;
                this.lastSpans = [];
                this.captionsLog = [];
                this.mergedCaption = [];
                this.subtitleText = null;
                this.checkSubtitleInterval = config.defaultCaptureInterval;
            }

            // Function to detect and get the caption spans as an array
            detectCaptionAsArray() {
                try {
                    let spans = document.querySelectorAll('div.ygicle.VbkSUe');
                    if (!spans || spans.length === 0) {
                        logger.debug("No caption spans found");
                        return [];
                    }
                    return Array.from(spans).map(span => span.innerText.trim()) || [];
                } catch (error) {
                    logger.error("Error detecting captions:", error);
                    return [];
                }
            }

            // Function to get absolute time in HH:MM:SS,mmm format (Real-Time)
            getCurrentTimestamp() {
                let now = new Date();
                let hours = String(now.getHours()).padStart(2, '0');
                let minutes = String(now.getMinutes()).padStart(2, '0');
                let seconds = String(now.getSeconds()).padStart(2, '0');
                let millis = String(now.getMilliseconds()).padStart(3, '0');

                return `${hours}:${minutes}:${seconds},${millis}`; // Absolute time format
            }

            // Function to save new words with timestamp
            saveCaptionByWords(timestamp, newText) {
                this.captionsLog.push({ time: timestamp, text: newText });
                logger.debug(`${timestamp} --> ${newText}`); // Print in subtitle format
            }

            formatTimestamp(ms) {
                const h = String(Math.floor(ms / 3600000)).padStart(2, "0");
                ms %= 3600000;
                const m = String(Math.floor(ms / 60000)).padStart(2, "0");
                ms %= 60000;
                const s = String(Math.floor(ms / 1000)).padStart(2, "0");
                const msStr = String(ms % 1000).padStart(3, "0");
                return `${h}:${m}:${s},${msStr}`;
            }

            mergeCaptions(captions, gap = 1000) {
                if (!captions || captions.length === 0) return [];

                const parseTime = (timestamp) => {
                    const [h, m, sMs] = timestamp.split(":");
                    const [s, ms] = sMs.split(",");
                    return (
                        parseInt(h) * 3600000 +
                        parseInt(m) * 60000 +
                        parseInt(s) * 1000 +
                        parseInt(ms)
                    );
                };

                const merged = [];
                let current = {
                    start: parseTime(captions[0].time),
                    end: parseTime(captions[0].time),
                    texts: [captions[0].text]
                };

                for (let i = 1; i < captions.length; i++) {
                    const entry = captions[i];
                    const time = parseTime(entry.time);
                    const timeDiff = time - current.end;

                    if (timeDiff > gap) {
                        merged.push({
                            start: this.formatTimestamp(current.start),
                            end: this.formatTimestamp(current.end),
                            text: current.texts.join(" ")
                        });

                        current = {
                            start: time,
                            end: time,
                            texts: [entry.text]
                        };
                    } else {
                        current.end = time;
                        current.texts.push(entry.text);
                    }
                }

                // Push the last group
                merged.push({
                    start: this.formatTimestamp(current.start),
                    end: this.formatTimestamp(current.end),
                    text: current.texts.join(" ")
                });

                this.mergedCaption = merged;

                return merged;
            }


            printMergedSubtitle() {
                const merged = this.mergeCaptions(this.getCapturedCaptions(), 1000);
                if (merged.length === 0) {
                    logger.debug("No subtitles to print.");
                    return;
                }

                const parseTime = (timestamp) => {
                    const [h, m, sMs] = timestamp.split(":");
                    const [s, ms] = sMs.split(",");
                    return (
                        parseInt(h) * 3600000 +
                        parseInt(m) * 60000 +
                        parseInt(s) * 1000 +
                        parseInt(ms)
                    );
                };

                const baseTime = parseTime(merged[0].start); // time of first word

                const toRelative = (absTime) => {
                    const ms = parseTime(absTime) - baseTime;
                    return this.formatTimestamp(ms);
                };

                this.subtitleText = merged.map((item, index) =>
                    `${index + 1}\n${toRelative(item.start)} --> ${toRelative(item.end)}\n${item.text}\n`
                ).join("\n");

                logger.debug(this.subtitleText);

                return this.subtitleText;
            }

            generateSrtFilename() {
                const now = new Date();
                const pad = (n) => String(n).padStart(2, '0');
                return `meet_caption_${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}.srt`;
            }

            downloadSrtFile() {
                if (!this.subtitleText) {
                    logger.warn("No subtitle content found. Please call printMergedSubtitle() first.");
                    return;
                }

                const filename = this.generateSrtFilename();
                const blob = new Blob([this.subtitleText], { type: "text/plain" });
                const url = URL.createObjectURL(blob);

                const a = document.createElement("a");
                a.href = url;
                a.download = filename;
                a.click();

                URL.revokeObjectURL(url);
            }



            // Function to start capturing captions
            startCapturing() {
                if (this.isCapturing) {
                    logger.debug("Caption capturing is already running.");
                    return;
                }

                this.isCapturing = true;
                logger.debug("Caption capturing started.");

                this.intervalId = setInterval(() => {
                    let currentSpans = this.detectCaptionAsArray();

                    let lastOldSpan = this.lastSpans[this.lastSpans.length - 1] || "";
                    let lastNewSpan = currentSpans[currentSpans.length - 1] || "";

                    let isLastCaptionChanged = lastNewSpan !== lastOldSpan;

                    if (isLastCaptionChanged) {
                        this.lastSpans = currentSpans; // Update stored spans
                        let timestamp = this.getCurrentTimestamp();

                        // Get only the new part of the last span
                        let newText = lastNewSpan.replace(lastOldSpan, "").trim();

                        if (newText) {
                            logger.debug(newText)
                            const wordCount = newText.trim().split(/\s+/).length;
                            const BurstTextCount = 80;
                            if (wordCount <= (this.checkSubtitleInterval / 100) * BurstTextCount) {
                                this.saveCaptionByWords(timestamp, newText);
                            } else {
                                logger.debug(`Skipped burst text with ${wordCount} words at ${timestamp}`);
                            }
                        }
                    }
                }, this.checkSubtitleInterval); // Runs every 100ms for better precision
            }

            // Function to stop capturing captions
            stopCapturing() {
                if (!this.isCapturing) {
                    logger.debug("Caption capturing is not running.");
                    return;
                }

                this.isCapturing = false;
                clearInterval(this.intervalId);
                logger.debug("Caption capturing stopped.");
            }

            // Function to get the saved captions log
            getCapturedCaptions() {
                return this.captionsLog;
            }
        }

        // Initialize CaptionCapture and message listener
        const captionCapture = new CaptionCapture();
        logger.debug("CaptionCapture initialized");

        chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
            logger.debug("start of listener in content.js");

            if (message.action === "startCapture") {
                logger.debug("startCapture content.js");
                captionCapture.startCapturing();
                sendResponse({ status: "started" });
            } else if (message.action === "stopCapture") {
                logger.debug("stopCapture content.js");
                captionCapture.stopCapturing();
                sendResponse({ status: "stopped" });
            } else if (message.action === "downloadSrt") {
                alert("Downloading SRT file...");
                logger.debug("downloadSrt content.js");
                captionCapture.printMergedSubtitle();
                captionCapture.downloadSrtFile();
                sendResponse({ status: "downloadStarted" });
            }
        });

        logger.debug("Message listener set up");
    } catch (error) {
        logger.error("Error loading modules:", error);
    }
})();

