import { getDB } from "../app";
import { getSettings } from "../storage/db";

let audioCtx: AudioContext | null = null;
let ambientAudio: HTMLAudioElement | null = null;
let ambientStarted = false;

/**
 * Creates or resumes AudioContext on first user interaction.
 * Call from a user-interaction event handler (click, keydown) to comply
 * with browser autoplay policy.
 */
export function initAudioOnInteraction(): void {
	try {
		if (!audioCtx) {
			audioCtx = new AudioContext();
		}
		if (audioCtx.state === "suspended") {
			audioCtx.resume();
		}
	} catch {
		// AudioContext not supported — silent fallback
	}
}

/**
 * Start ambient forest sounds (loops quietly in background).
 * Called once on first user interaction alongside AudioContext init.
 */
export async function startAmbientSound(): Promise<void> {
	if (ambientStarted) return;
	ambientStarted = true;
	try {
		if (!(await isAudioEnabled())) return;
		ambientAudio = new Audio("/forest-birds.mp3");
		ambientAudio.loop = true;
		ambientAudio.volume = 0.08;
		ambientAudio.play().catch(() => {
			// Autoplay blocked — will retry on next interaction
			ambientStarted = false;
		});
	} catch {
		ambientStarted = false;
	}
}

/**
 * Update ambient sound state based on mute toggle.
 */
export function setAmbientMuted(muted: boolean): void {
	if (!ambientAudio) return;
	if (muted) {
		ambientAudio.pause();
	} else {
		ambientAudio.play().catch(() => {});
	}
}

async function isAudioEnabled(): Promise<boolean> {
	try {
		const db = getDB();
		const settings = await getSettings(db);
		return settings?.audioEnabled !== false;
	} catch {
		return true; // default to enabled if settings unavailable
	}
}

function ensureAudioCtx(): AudioContext | null {
	if (!audioCtx) {
		initAudioOnInteraction();
	}
	return audioCtx;
}

/**
 * Short confirmation beep: 800Hz sine, 0.1s, gain 0.15.
 */
export async function playCorrectSound(): Promise<void> {
	try {
		if (!(await isAudioEnabled())) return;
		const ctx = ensureAudioCtx();
		if (!ctx) return;

		const osc = ctx.createOscillator();
		const gain = ctx.createGain();

		osc.type = "sine";
		osc.frequency.value = 800;
		gain.gain.value = 0.15;

		osc.connect(gain);
		gain.connect(ctx.destination);

		const now = ctx.currentTime;
		// Ramp gain to 0 over last 0.02s for click-free cutoff
		gain.gain.setValueAtTime(0.15, now);
		gain.gain.linearRampToValueAtTime(0, now + 0.1);

		osc.start(now);
		osc.stop(now + 0.1);
	} catch {
		// Never throw to caller
	}
}

/**
 * Ascending reward tone sequence: C5 E5 G5 C6, ~0.8s total.
 */
export async function playRewardSound(): Promise<void> {
	try {
		if (!(await isAudioEnabled())) return;
		const ctx = ensureAudioCtx();
		if (!ctx) return;

		const notes = [523, 659, 784, 1047]; // C5, E5, G5, C6
		const noteSpacing = 0.2;
		const noteDuration = 0.15;
		const now = ctx.currentTime;

		for (const [i, freq] of notes.entries()) {
			const osc = ctx.createOscillator();
			const gain = ctx.createGain();

			osc.type = "sine";
			osc.frequency.value = freq;
			gain.gain.value = 0.12;

			osc.connect(gain);
			gain.connect(ctx.destination);

			const startTime = now + i * noteSpacing;
			gain.gain.setValueAtTime(0.12, startTime);
			gain.gain.linearRampToValueAtTime(0, startTime + noteDuration);

			osc.start(startTime);
			osc.stop(startTime + noteDuration);
		}
	} catch {
		// Never throw to caller
	}
}

/**
 * Forest element sound: filtered white noise, bandpass 3000Hz, 0.5s.
 */
export async function playForestElementSound(): Promise<void> {
	try {
		if (!(await isAudioEnabled())) return;
		const ctx = ensureAudioCtx();
		if (!ctx) return;

		// Create white noise buffer
		const bufferSize = ctx.sampleRate * 0.5; // 0.5 seconds
		const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
		const data = buffer.getChannelData(0);
		for (let i = 0; i < bufferSize; i++) {
			data[i] = Math.random() * 2 - 1;
		}

		const source = ctx.createBufferSource();
		source.buffer = buffer;

		const filter = ctx.createBiquadFilter();
		filter.type = "bandpass";
		filter.frequency.value = 3000;
		filter.Q.value = 0.5;

		const gain = ctx.createGain();
		gain.gain.value = 0.1;

		source.connect(filter);
		filter.connect(gain);
		gain.connect(ctx.destination);

		const now = ctx.currentTime;
		// Ramp down gain over duration
		gain.gain.setValueAtTime(0.1, now);
		gain.gain.linearRampToValueAtTime(0, now + 0.5);

		source.start(now);
		source.stop(now + 0.5);
	} catch {
		// Never throw to caller
	}
}
