import type { Song, SongType } from '../types/song';

export interface ScannedTrack {
  id: string;
  name: string;
  artist: string;
  album: string;
  duration: number;
  file: File;
  fileHandle?: FileSystemFileHandle;
  url?: string;
}

const AUDIO_EXTS = new Set(['.mp3', '.flac', '.wav', '.ogg', '.aac', '.m4a', '.wma', '.ape', '.opus']);

function isAudioFile(name: string): boolean {
  const dot = name.lastIndexOf('.');
  if (dot === -1) return false;
  return AUDIO_EXTS.has(name.slice(dot).toLowerCase());
}

function parseFileName(name: string): { title: string; artist: string } {
  const dot = name.lastIndexOf('.');
  const base = dot !== -1 ? name.slice(0, dot) : name;
  const separators = [' - ', ' – ', ' — ', ' -', '-', ' / ', '/'];
  for (const sep of separators) {
    const idx = base.indexOf(sep);
    if (idx > 0 && idx < base.length - sep.length) {
      return { artist: base.slice(0, idx).trim(), title: base.slice(idx + sep.length).trim() };
    }
  }
  return { title: base, artist: '' };
}

function genId(): string {
  return 'local-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 8);
}

async function getDuration(file: File): Promise<number> {
  return new Promise((resolve) => {
    const url = URL.createObjectURL(file);
    const audio = new Audio();
    audio.preload = 'metadata';
    const timer = setTimeout(() => { audio.src = ''; URL.revokeObjectURL(url); resolve(0); }, 5000);
    audio.onloadedmetadata = () => {
      clearTimeout(timer);
      const dur = Math.round(audio.duration * 1000);
      audio.src = '';
      URL.revokeObjectURL(url);
      resolve(dur);
    };
    audio.onerror = () => { clearTimeout(timer); audio.src = ''; URL.revokeObjectURL(url); resolve(0); };
    audio.src = url;
    audio.load();
  });
}

export async function scanFile(file: File, fileHandle?: FileSystemFileHandle): Promise<ScannedTrack | null> {
  if (!isAudioFile(file.name)) return null;
  const parsed = parseFileName(file.name);
  const duration = await getDuration(file);
  return {
    id: genId(),
    name: parsed.title || file.name,
    artist: parsed.artist || '未知歌手',
    album: '',
    duration,
    file,
    fileHandle,
  };
}

export async function scanDirectory(dirHandle: FileSystemDirectoryHandle, onProgress?: (name: string) => void): Promise<ScannedTrack[]> {
  const results: ScannedTrack[] = [];
  const entries: [string, FileSystemHandle][] = [];
  for await (const entry of (dirHandle as any).entries()) {
    entries.push(entry);
  }
  for (const [name, handle] of entries) {
    if (onProgress) onProgress(name);
    if (handle.kind === 'file') {
      const fileHandle = handle as FileSystemFileHandle;
      const file = await fileHandle.getFile();
      const track = await scanFile(file, fileHandle);
      if (track) results.push(track);
    } else if (handle.kind === 'directory') {
      const sub = await scanDirectory(handle as FileSystemDirectoryHandle, onProgress);
      results.push(...sub);
    }
  }
  return results;
}

export function trackToSong(track: ScannedTrack): Song {
  return {
    id: track.id,
    name: track.name,
    artist: track.artist,
    picUrl: '',
    url: track.url || '',
    duration: formatDuration(track.duration),
    source: 'local',
    type: 'Local' as SongType,
    albumName: track.album || null,
    quality: 'standard',
  };
}

export function formatDuration(ms: number): string {
  if (!ms || ms <= 0) return '0:00';
  const totalSec = Math.floor(ms / 1000);
  const min = Math.floor(totalSec / 60);
  const sec = totalSec % 60;
  return `${min}:${sec.toString().padStart(2, '0')}`;
}

const DB_NAME = 'qz-local-music';
const DB_VERSION = 1;
const STORE_NAME = 'tracks';

function openDB(): Promise<IDBDatabase> {
  return new Promise((resolve, reject) => {
    const req = indexedDB.open(DB_NAME, DB_VERSION);
    req.onupgradeneeded = () => {
      const db = req.result;
      if (!db.objectStoreNames.contains(STORE_NAME)) {
        db.createObjectStore(STORE_NAME, { keyPath: 'id' });
      }
    };
    req.onsuccess = () => resolve(req.result);
    req.onerror = () => reject(req.error);
  });
}

export async function saveTracks(tracks: ScannedTrack[]): Promise<void> {
  const db = await openDB();
  return new Promise((resolve, reject) => {
    const tx = db.transaction(STORE_NAME, 'readwrite');
    const store = tx.objectStore(STORE_NAME);
    for (const t of tracks) {
      store.put({ id: t.id, name: t.name, artist: t.artist, album: t.album, duration: t.duration });
    }
    tx.oncomplete = () => { db.close(); resolve(); };
    tx.onerror = () => { db.close(); reject(tx.error); };
  });
}

export async function loadTracks(): Promise<ScannedTrack[]> {
  try {
    const db = await openDB();
    return new Promise((resolve, reject) => {
      const tx = db.transaction(STORE_NAME, 'readonly');
      const store = tx.objectStore(STORE_NAME);
      const req = store.getAll();
      req.onsuccess = () => { db.close(); resolve(req.result || []); };
      req.onerror = () => { db.close(); reject(req.error); };
    });
  } catch {
    return [];
  }
}

export async function clearSavedTracks(): Promise<void> {
  try {
    const db = await openDB();
    return new Promise((resolve, reject) => {
      const tx = db.transaction(STORE_NAME, 'readwrite');
      tx.objectStore(STORE_NAME).clear();
      tx.oncomplete = () => { db.close(); resolve(); };
      tx.onerror = () => { db.close(); reject(tx.error); };
    });
  } catch { /* noop */ }
}

export function supportsDirectoryPicker(): boolean {
  return typeof (window as any).showDirectoryPicker === 'function';
}
