use crate::Args;
use crate::lyrics::Lyric;
use crate::lyrics_api::Response;
use crate::lyrics_api::genius::fetch_genius;
use crate::lyrics_api::lrclib::fetch_lrclib;
use crate::lyrics_api::musixmatch::fetch_musixmatch;
use crate::metadata::TrackMetadata;
use log::{error, info, warn};
use lyrx::{Lyrics, TimeTag};
use std::path::PathBuf;
use std::process::exit;
use walkdir::WalkDir;

pub fn open_path(path: &PathBuf, args: &Args) {
    let audio_extensions = ["mp3", "flac", "wav", "ogg", "m4a", "opus"];

    if path.is_dir() {
        info!("Scanning directory: {}", path.display());

        let walker = if args.recursive {
            WalkDir::new(path)
        } else {
            warn!("Directory scan without flag -r, subdirectories will not be read");
            WalkDir::new(path).max_depth(1)
        };

        let mut found = false;

        for entry in walker
            .into_iter()
            .filter_map(Result::ok)
            .filter(|e| e.file_type().is_file())
        {
            let path_buf = entry.path().to_path_buf();

            if let Some(ext) = path_buf.extension().and_then(|e| e.to_str())
                && audio_extensions.contains(&ext.to_lowercase().as_str())
            {
                found = true;
                process_file(&path_buf, args);
            }
        }

        if !found {
            error!("No audio files found in directory: {}", path.display());
            exit(1);
        }
    } else if path.is_file() {
        process_file(path, args);
    } else {
        error!("Invalid path: {}", args.path.display());
        exit(1);
    }
}

fn search_lyrics(path: &PathBuf, args: &Args) -> Option<Lyric> {
    info!("Searching lyrics for {}", path.display());

    let metadata = TrackMetadata::new(path);

    let response = if args.plain_only {
        fetch_genius(&metadata)
    } else if args.enhanced {
        fetch_musixmatch(&metadata)
    } else {
        fetch_lrclib(&metadata, args.plain_only)
    };

    match response {
        Ok(response) => {
            match &response {
                Response::None => {
                    error!("No lyrics found for {}", path.display());
                    return None;
                }
                Response::IsInstrumental => {
                    info!("Track is instrumental: {}", path.display());
                }
                Response::SyncedLyrics { synced, plain } if args.remove_empty_lines => {
                    let lyrics = Lyrics::from_str(synced).unwrap();
                    let mut new_lyrics = Lyrics::new();
                    for (position, line) in lyrics.to_vec() {
                        let position: u64 = position;
                        if line.is_empty() {
                            continue;
                        }
                        let formatted_line = format!(" {}", &line);
                        new_lyrics
                            .add_timed_line(TimeTag::new(position as i64), formatted_line)
                            .unwrap();
                    }
                    info!("Removed empty lines for {}", path.display());
                    let new_response = Response::SyncedLyrics {
                        synced: new_lyrics.to_string(),
                        plain: plain.to_string(),
                    };

                    if args.output()
                        && let Some(text) = new_response.output()
                    {
                        println!("{}", text);
                    }

                    return Some(Lyric::new(new_response, path.clone()));
                }
                _ => {}
            }
            if args.output()
                && let Some(text) = response.output()
            {
                println!("{}", text);
            }

            Some(Lyric::new(response, path.clone()))
        }
        Err(e) => {
            error!("Error: {} for {}", e.message(), path.display());
            None
        }
    }
}

fn process_file(path: &PathBuf, args: &Args) {
    let lyric = match search_lyrics(path, args) {
        Some(l) => l,
        None => return,
    };

    if args.write_as_file() {
        match lyric.save_to_file() {
            Ok(()) => info!("Lyrics file saved for {}", path.display()),
            Err(e) => error!(
                "Failed to save lyrics file for {}: {}",
                path.display(),
                e.message()
            ),
        }
    } else {
        match lyric.embed() {
            Ok(()) => info!("Lyrics embedded for {}", path.display()),
            Err(e) => error!(
                "Error to embed lyrics for {}: {}",
                path.display(),
                e.message()
            ),
        }
    }
}
