/**
 * Keyboard Tap-to-Record Engine (鍵盤逐字打點錄製)
 */
class KTVRecorder {
  constructor(state, player) {
    this.state = state;
    this.player = player;
    this.audioCtx = null;
    
    this.isKeyHeld = false;
    this.activeKey = null;
    this.isHoldingLastWord = false;
    this.heldWord = null;
    this.heldWordSIdx = null;
    this.heldWordWIdx = null;
    this.sentenceAdvanceTimer = null;

    this.initAudioContext();
    this.initUI();
    this.initKeyboardEvents();

    // 只有在長按每句的「最後一字」時，才隨播放即時延伸長度
    this.state.on("timeUpdate", (curTime) => {
      if (this.isHoldingLastWord && this.heldWord) {
        this.heldWord.endTime = Math.max(this.heldWord.startTime + 0.1, curTime);
        this.state.emit("timingUpdated", { sIdx: this.heldWordSIdx, wIdx: this.heldWordWIdx });
      }
    });
  }

  initAudioContext() {
    try {
      const AudioContext = window.AudioContext || window.webkitAudioContext;
      this.audioCtx = new AudioContext();
    } catch (e) {
      console.log("Web Audio not supported:", e);
    }
  }

  playTapSound() {
    if (!this.audioCtx) return;
    if (this.audioCtx.state === 'suspended') {
      this.audioCtx.resume();
    }
    const osc = this.audioCtx.createOscillator();
    const gain = this.audioCtx.createGain();
    osc.type = "sine";
    osc.frequency.setValueAtTime(880, this.audioCtx.currentTime); // 880Hz A5 chirp
    gain.gain.setValueAtTime(0.2, this.audioCtx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.001, this.audioCtx.currentTime + 0.08);
    osc.connect(gain);
    gain.connect(this.audioCtx.destination);
    osc.start();
    osc.stop(this.audioCtx.currentTime + 0.08);
  }

  initUI() {
    const tapBtn = document.getElementById("btn-tap-action");
    const rewindBtn = document.getElementById("btn-tap-rewind");
    const resetLineBtn = document.getElementById("btn-tap-reset-line");
    const nextLineBtn = document.getElementById("btn-tap-next-line");
    const prevLineBtn = document.getElementById("btn-tap-prev-line");

    if (tapBtn) {
      // Support holding down on-screen button with mouse or touch
      tapBtn.addEventListener("mousedown", (e) => {
        e.preventDefault();
        if (this.isKeyHeld) return;
        this.handleTapDown();
      });
      tapBtn.addEventListener("mouseup", (e) => {
        e.preventDefault();
        this.handleTapUp();
      });
      tapBtn.addEventListener("mouseleave", () => {
        if (this.isKeyHeld) this.handleTapUp();
      });
      tapBtn.addEventListener("touchstart", (e) => {
        e.preventDefault();
        if (this.isKeyHeld) return;
        this.handleTapDown();
      }, { passive: false });
      tapBtn.addEventListener("touchend", (e) => {
        e.preventDefault();
        this.handleTapUp();
      });
    }

    if (rewindBtn) rewindBtn.addEventListener("click", () => { this.rewind5s(); rewindBtn.blur(); });
    if (resetLineBtn) resetLineBtn.addEventListener("click", () => { this.resetCurrentLine(); resetLineBtn.blur(); });
    if (nextLineBtn) nextLineBtn.addEventListener("click", () => { this.gotoNextSentence(); nextLineBtn.blur(); });
    if (prevLineBtn) prevLineBtn.addEventListener("click", () => { this.gotoPrevSentence(); prevLineBtn.blur(); });

    // Speed buttons
    const speedSelect = document.getElementById("tap-playback-speed");
    if (speedSelect) {
      speedSelect.addEventListener("change", (e) => {
        const rate = parseFloat(e.target.value);
        this.player.setPlaybackRate(rate);
        speedSelect.blur();
      });
    }

    this.state.on("sentencesChanged", () => this.updateHUD());
    this.updateHUD();
  }

  initKeyboardEvents() {
    // Intercept in capture phase so focused buttons NEVER steal Space or Enter
    window.addEventListener("keydown", (e) => {
      // Ignore if user is currently typing in an input field or textarea
      if (["INPUT", "TEXTAREA"].includes(document.activeElement.tagName)) return;

      const isTapKey = (
        e.code === "Space" || 
        e.code === "Enter" || 
        e.code === "NumpadEnter" || 
        e.key === " " || 
        e.key === "Enter"
      );

      // When in timeline mode, Space acts as play/pause toggle
      if (this.state.currentMode !== "tap") {
        if (e.code === "Space" || e.key === " ") {
          e.preventDefault();
          this.player.togglePlay();
          if (document.activeElement && document.activeElement.blur) document.activeElement.blur();
        }
        return;
      }

      // In Tap Recording Mode:
      if (isTapKey) {
        e.preventDefault();
        e.stopPropagation();
        if (document.activeElement && document.activeElement.blur) {
          document.activeElement.blur();
        }

        // ========================================================
        // 核心防連點機制：長按不放時徹底封鎖作業系統自動重複 (Auto-Repeat)
        // 1. e.repeat 檢查瀏覽器/OS 標記
        // 2. this.isKeyHeld 檢查是否正在按住同一個字
        // 兩者只要任一為真，直接 return，杜絕長按變連續按問題！
        // ========================================================
        if (e.repeat || this.isKeyHeld) {
          return;
        }

        this.activeKey = e.code || e.key;
        this.handleTapDown();
      } else if (e.code === "KeyR" || e.key === "r" || e.key === "R") {
        e.preventDefault();
        this.resetCurrentLine();
      } else if (e.code === "ArrowLeft") {
        e.preventDefault();
        this.rewind5s();
      } else if (e.code === "ArrowRight") {
        e.preventDefault();
        this.player.seek(this.player.video.currentTime + 3);
      }
    }, true);

    // Keyup event: 放開按鍵時精準記錄該字的結束時間！
    window.addEventListener("keyup", (e) => {
      if (this.state.currentMode !== "tap") return;
      if (["INPUT", "TEXTAREA"].includes(document.activeElement.tagName)) return;

      const isTapKey = (
        e.code === "Space" || 
        e.code === "Enter" || 
        e.code === "NumpadEnter" || 
        e.key === " " || 
        e.key === "Enter" ||
        (this.activeKey && (e.code === this.activeKey || e.key === this.activeKey))
      );

      if (isTapKey) {
        e.preventDefault();
        e.stopPropagation();
        this.activeKey = null;
        this.handleTapUp();
      }
    }, true);

    // 視窗失去焦點或分頁切換時，自動安全釋放長按
    window.addEventListener("blur", () => {
      if (this.isKeyHeld) {
        this.activeKey = null;
        this.handleTapUp();
      }
    });

    document.addEventListener("visibilitychange", () => {
      if (document.hidden && this.isKeyHeld) {
        this.activeKey = null;
        this.handleTapUp();
      }
    });
  }

  handleTapDown() {
    // 嚴防重複進入：若鍵盤當前已經處於按住中，封鎖 OS 自動重複連點
    if (this.isKeyHeld) return;
    this.isKeyHeld = true;

    if (this.sentenceAdvanceTimer) {
      clearTimeout(this.sentenceAdvanceTimer);
      this.sentenceAdvanceTimer = null;
    }

    // If video is paused, start playing automatically
    if (this.player.video.paused) {
      this.player.play();
    }

    this.playTapSound();

    const sIdx = this.state.activeSentenceIndex;
    const sentences = this.state.sentences;
    if (!sentences || !sentences[sIdx]) return;

    const sentence = sentences[sIdx];
    const words = sentence.words || [];
    let wIdx = this.state.activeWordIndex;
    const curTime = this.player.video.currentTime;

    // If current word is within range
    if (wIdx < words.length) {
      // 1. 若不是第一個字，將前一個字的結束時間無縫銜接為當前按下時間（變色最順暢！）
      if (wIdx > 0 && words[wIdx - 1]) {
        words[wIdx - 1].endTime = Math.max(words[wIdx - 1].startTime + 0.05, curTime);
        this.state.emit("timingUpdated", { sIdx, wIdx: wIdx - 1 });
      }

      const w = words[wIdx];
      w.startTime = curTime;
      const isLastWord = (wIdx === words.length - 1);

      if (isLastWord) {
        // ★ 句末最後一字：記錄按住狀態，等待放開按鍵時精準鎖定結束時間！
        w.endTime = curTime + 0.15;
        this.isHoldingLastWord = true;
        this.heldWord = w;
        this.heldWordSIdx = sIdx;
        this.heldWordWIdx = wIdx;
        // activeWordIndex 留在最後一字，直到放開按鍵
      } else {
        // ★ 句中其它字：照原來方式，立即推進到下一字，讓後續字按下時自動無縫銜接！
        w.endTime = curTime + 0.25;
        this.isHoldingLastWord = false;
        this.heldWord = null;
        this.state.activeWordIndex = wIdx + 1;
      }

      this.state.emit("timingUpdated", { sIdx, wIdx });
      this.updateHUD();
    } else {
      // 本句已打點完成，再次按下直接前進至下一句並打點第一字
      if (sIdx < sentences.length - 1) {
        this.state.activeSentenceIndex = sIdx + 1;
        this.state.activeWordIndex = 0;
        this.isKeyHeld = false;
        this.handleTapDown();
      }
    }
  }

  handleTapUp() {
    this.isKeyHeld = false;

    // 只有在長按每句的「最後一字」時，才在放開按鍵時結算結束時間
    if (this.isHoldingLastWord && this.heldWord) {
      const releaseTime = this.player.video.currentTime;
      const w = this.heldWord;
      const sIdx = this.heldWordSIdx;
      const wIdx = this.heldWordWIdx;

      // 放開時鎖定最後一字的結束時間！
      w.endTime = Math.max(w.startTime + 0.12, releaseTime);

      // 本句全部字已打點完成，推進 activeWordIndex
      this.state.activeWordIndex = wIdx + 1;

      this.isHoldingLastWord = false;
      this.heldWord = null;
      this.heldWordSIdx = null;
      this.heldWordWIdx = null;

      // 句子完成後，延遲 350ms 自動跳到下一句
      if (this.sentenceAdvanceTimer) clearTimeout(this.sentenceAdvanceTimer);
      this.sentenceAdvanceTimer = setTimeout(() => {
        if (this.state.activeSentenceIndex < this.state.sentences.length - 1) {
          this.state.activeSentenceIndex++;
          this.state.activeWordIndex = 0;
          this.updateHUD();
        }
      }, 350);

      this.state.emit("timingUpdated", { sIdx, wIdx });
      this.updateHUD();
    }
  }

  resetCurrentLine() {
    if (this.sentenceAdvanceTimer) {
      clearTimeout(this.sentenceAdvanceTimer);
      this.sentenceAdvanceTimer = null;
    }
    this.isKeyHeld = false;
    this.isHoldingLastWord = false;
    this.heldWord = null;
    this.activeKey = null;

    const sIdx = this.state.activeSentenceIndex;
    const sentence = this.state.sentences[sIdx];
    if (!sentence) return;

    // Reset words timing
    (sentence.words || []).forEach(w => {
      w.startTime = 0;
      w.endTime = 0;
    });

    this.state.activeWordIndex = 0;

    // Seek video back ~3 seconds before or to sentence start
    const prevSentence = sIdx > 0 ? this.state.sentences[sIdx - 1] : null;
    let seekBack = 0;
    if (prevSentence && prevSentence.words && prevSentence.words.length > 0) {
      seekBack = Math.max(0, prevSentence.words[prevSentence.words.length - 1].endTime);
    } else {
      seekBack = Math.max(0, this.player.video.currentTime - 4);
    }

    this.player.seek(seekBack);
    this.state.emit("timingUpdated", { sIdx });
    this.updateHUD();
  }

  rewind5s() {
    this.player.seek(Math.max(0, this.player.video.currentTime - 5));
  }

  gotoNextSentence() {
    if (this.sentenceAdvanceTimer) {
      clearTimeout(this.sentenceAdvanceTimer);
      this.sentenceAdvanceTimer = null;
    }
    this.isKeyHeld = false;
    this.isHoldingLastWord = false;
    this.heldWord = null;
    this.activeKey = null;

    if (this.state.activeSentenceIndex < this.state.sentences.length - 1) {
      this.state.activeSentenceIndex++;
      this.state.activeWordIndex = 0;
      this.updateHUD();
    }
  }

  gotoPrevSentence() {
    if (this.sentenceAdvanceTimer) {
      clearTimeout(this.sentenceAdvanceTimer);
      this.sentenceAdvanceTimer = null;
    }
    this.isKeyHeld = false;
    this.isHoldingLastWord = false;
    this.heldWord = null;
    this.activeKey = null;

    if (this.state.activeSentenceIndex > 0) {
      this.state.activeSentenceIndex--;
      this.state.activeWordIndex = 0;
      this.updateHUD();
    }
  }

  updateHUD() {
    const container = document.getElementById("tap-words-display");
    const metaEl = document.getElementById("tap-sentence-meta");
    if (!container) return;

    const sIdx = this.state.activeSentenceIndex;
    const sentences = this.state.sentences;

    if (!sentences || sentences.length === 0 || !sentences[sIdx]) {
      container.innerHTML = `<span class="tap-char">請先匯入歌詞</span>`;
      if (metaEl) metaEl.innerText = "尚未載入歌詞";
      return;
    }

    const sentence = sentences[sIdx];
    const words = sentence.words || [];
    const wIdx = this.state.activeWordIndex;
    const isLine1 = (sIdx % 2 === 0);

    const singerNameMap = { male: "男唱", female: "女唱", duet: "合唱" };
    const singerStr = singerNameMap[sentence.singer] || "合唱";

    if (metaEl) {
      metaEl.innerText = `第 ${sIdx + 1} / ${sentences.length} 句【${singerStr}】(位於${isLine1 ? '第 1 行' : '第 2 行'})`;
    }

    container.innerHTML = "";

    // Show prefix chip if present (non-clickable indicator)
    if (sentence.prefix) {
      const pfxSpan = document.createElement("span");
      const singerColor = sentence.singer === 'male' ? 'var(--accent-cyan)' : sentence.singer === 'female' ? 'var(--accent-pink)' : 'var(--accent-green)';
      pfxSpan.className = "tap-prefix-indicator";
      pfxSpan.style.display = "inline-flex";
      pfxSpan.style.alignItems = "center";
      pfxSpan.style.marginRight = "10px";
      pfxSpan.style.padding = "4px 10px";
      pfxSpan.style.borderRadius = "6px";
      pfxSpan.style.fontWeight = "bold";
      pfxSpan.style.fontSize = "1.2rem";
      pfxSpan.style.color = singerColor;
      pfxSpan.style.background = "rgba(255, 255, 255, 0.07)";
      pfxSpan.style.border = `1.5px solid ${singerColor}`;
      pfxSpan.innerText = sentence.prefix;
      pfxSpan.title = "句首演唱者標籤（常駐變色顯示，無需打點）";
      container.appendChild(pfxSpan);
    }

    words.forEach((w, idx) => {
      const span = document.createElement("span");
      span.className = "tap-char";
      span.innerText = w.text;

      const hasTime = (w.startTime > 0 || w.endTime > 0);
      if (hasTime) {
        span.title = `【${w.text}】${(w.startTime || 0).toFixed(2)}s ~ ${(w.endTime || 0).toFixed(2)}s (點擊跳轉)`;
      } else {
        span.title = `【${w.text}】尚未打點 (點擊選擇以此字開始)`;
      }

      if (this.isHoldingLastWord && idx === this.heldWordWIdx) {
        span.classList.add("active-holding");
      } else if (idx < wIdx) {
        span.classList.add("recorded");
      }

      // Click character to seek to its position and set as current word
      span.addEventListener("click", () => {
        this.state.activeWordIndex = idx;
        if (w.startTime > 0) {
          this.player.seek(w.startTime);
        }
        this.updateHUD();
      });

      container.appendChild(span);
    });
  }
}

window.KTVRecorder = KTVRecorder;
