use std::path::Path;

/// Programming language detected from file extension.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Language {
    Rust,
    Python,
    JavaScript,
    TypeScript,
    Go,
    C,
    Cpp,
    CSharp,
    Java,
    Kotlin,
    Swift,
    Ruby,
    Php,
    Scala,
    Haskell,
    Elixir,
    Erlang,
    Clojure,
    Lua,
    Perl,
    R,
    Julia,
    Dart,
    Zig,
    Nim,
    OCaml,
    FSharp,
    Sql,
    Shell,
    PowerShell,
    Html,
    Css,
    Scss,
    Less,
    Json,
    Yaml,
    Toml,
    Xml,
    Markdown,
    Dockerfile,
    Makefile,
    Nix,
    Protobuf,
    GraphQL,
    Vue,
    Svelte,
    Astro,
    Jsx,
    Tsx,
}

impl Language {
    /// Detect language from a file path based on extension.
    pub fn from_path(path: &Path) -> Option<Self> {
        let ext = path.extension()?.to_str()?.to_lowercase();
        Self::from_extension(&ext).or_else(|| {
            // Check filename for special cases
            let filename = path.file_name()?.to_str()?;
            Self::from_filename(filename)
        })
    }

    /// Detect language from file extension.
    pub fn from_extension(ext: &str) -> Option<Self> {
        match ext.to_lowercase().as_str() {
            "rs" => Some(Self::Rust),
            "py" | "pyi" | "pyw" => Some(Self::Python),
            "js" | "mjs" | "cjs" => Some(Self::JavaScript),
            "ts" | "mts" | "cts" => Some(Self::TypeScript),
            "jsx" => Some(Self::Jsx),
            "tsx" => Some(Self::Tsx),
            "go" => Some(Self::Go),
            "c" | "h" => Some(Self::C),
            "cpp" | "cc" | "cxx" | "hpp" | "hh" | "hxx" => Some(Self::Cpp),
            "cs" => Some(Self::CSharp),
            "java" => Some(Self::Java),
            "kt" | "kts" => Some(Self::Kotlin),
            "swift" => Some(Self::Swift),
            "rb" | "rake" => Some(Self::Ruby),
            "php" => Some(Self::Php),
            "scala" | "sc" => Some(Self::Scala),
            "hs" | "lhs" => Some(Self::Haskell),
            "ex" | "exs" => Some(Self::Elixir),
            "erl" | "hrl" => Some(Self::Erlang),
            "clj" | "cljs" | "cljc" | "edn" => Some(Self::Clojure),
            "lua" => Some(Self::Lua),
            "pl" | "pm" => Some(Self::Perl),
            "r" => Some(Self::R),
            "jl" => Some(Self::Julia),
            "dart" => Some(Self::Dart),
            "zig" => Some(Self::Zig),
            "nim" => Some(Self::Nim),
            "ml" | "mli" => Some(Self::OCaml),
            "fs" | "fsi" | "fsx" => Some(Self::FSharp),
            "sql" => Some(Self::Sql),
            "sh" | "bash" | "zsh" | "fish" => Some(Self::Shell),
            "ps1" | "psm1" | "psd1" => Some(Self::PowerShell),
            "html" | "htm" => Some(Self::Html),
            "css" => Some(Self::Css),
            "scss" | "sass" => Some(Self::Scss),
            "less" => Some(Self::Less),
            "json" | "jsonc" => Some(Self::Json),
            "yaml" | "yml" => Some(Self::Yaml),
            "toml" => Some(Self::Toml),
            "xml" | "xsl" | "xslt" => Some(Self::Xml),
            "md" | "markdown" => Some(Self::Markdown),
            "dockerfile" => Some(Self::Dockerfile),
            "nix" => Some(Self::Nix),
            "proto" => Some(Self::Protobuf),
            "graphql" | "gql" => Some(Self::GraphQL),
            "vue" => Some(Self::Vue),
            "svelte" => Some(Self::Svelte),
            "astro" => Some(Self::Astro),
            _ => None,
        }
    }

    /// Detect language from filename (for files without extensions).
    pub fn from_filename(name: &str) -> Option<Self> {
        match name {
            "Dockerfile" | "dockerfile" => Some(Self::Dockerfile),
            "Makefile" | "makefile" | "GNUmakefile" => Some(Self::Makefile),
            ".bashrc" | ".bash_profile" | ".zshrc" | ".profile" => Some(Self::Shell),
            _ => None,
        }
    }

    /// Get the canonical file extension for this language.
    pub fn extension(&self) -> &'static str {
        match self {
            Self::Rust => "rs",
            Self::Python => "py",
            Self::JavaScript => "js",
            Self::TypeScript => "ts",
            Self::Jsx => "jsx",
            Self::Tsx => "tsx",
            Self::Go => "go",
            Self::C => "c",
            Self::Cpp => "cpp",
            Self::CSharp => "cs",
            Self::Java => "java",
            Self::Kotlin => "kt",
            Self::Swift => "swift",
            Self::Ruby => "rb",
            Self::Php => "php",
            Self::Scala => "scala",
            Self::Haskell => "hs",
            Self::Elixir => "ex",
            Self::Erlang => "erl",
            Self::Clojure => "clj",
            Self::Lua => "lua",
            Self::Perl => "pl",
            Self::R => "r",
            Self::Julia => "jl",
            Self::Dart => "dart",
            Self::Zig => "zig",
            Self::Nim => "nim",
            Self::OCaml => "ml",
            Self::FSharp => "fs",
            Self::Sql => "sql",
            Self::Shell => "sh",
            Self::PowerShell => "ps1",
            Self::Html => "html",
            Self::Css => "css",
            Self::Scss => "scss",
            Self::Less => "less",
            Self::Json => "json",
            Self::Yaml => "yaml",
            Self::Toml => "toml",
            Self::Xml => "xml",
            Self::Markdown => "md",
            Self::Dockerfile => "dockerfile",
            Self::Makefile => "makefile",
            Self::Nix => "nix",
            Self::Protobuf => "proto",
            Self::GraphQL => "graphql",
            Self::Vue => "vue",
            Self::Svelte => "svelte",
            Self::Astro => "astro",
        }
    }

    /// Get a human-readable name for this language.
    pub fn name(&self) -> &'static str {
        match self {
            Self::Rust => "Rust",
            Self::Python => "Python",
            Self::JavaScript => "JavaScript",
            Self::TypeScript => "TypeScript",
            Self::Jsx => "JSX",
            Self::Tsx => "TSX",
            Self::Go => "Go",
            Self::C => "C",
            Self::Cpp => "C++",
            Self::CSharp => "C#",
            Self::Java => "Java",
            Self::Kotlin => "Kotlin",
            Self::Swift => "Swift",
            Self::Ruby => "Ruby",
            Self::Php => "PHP",
            Self::Scala => "Scala",
            Self::Haskell => "Haskell",
            Self::Elixir => "Elixir",
            Self::Erlang => "Erlang",
            Self::Clojure => "Clojure",
            Self::Lua => "Lua",
            Self::Perl => "Perl",
            Self::R => "R",
            Self::Julia => "Julia",
            Self::Dart => "Dart",
            Self::Zig => "Zig",
            Self::Nim => "Nim",
            Self::OCaml => "OCaml",
            Self::FSharp => "F#",
            Self::Sql => "SQL",
            Self::Shell => "Shell",
            Self::PowerShell => "PowerShell",
            Self::Html => "HTML",
            Self::Css => "CSS",
            Self::Scss => "SCSS",
            Self::Less => "Less",
            Self::Json => "JSON",
            Self::Yaml => "YAML",
            Self::Toml => "TOML",
            Self::Xml => "XML",
            Self::Markdown => "Markdown",
            Self::Dockerfile => "Dockerfile",
            Self::Makefile => "Makefile",
            Self::Nix => "Nix",
            Self::Protobuf => "Protocol Buffers",
            Self::GraphQL => "GraphQL",
            Self::Vue => "Vue",
            Self::Svelte => "Svelte",
            Self::Astro => "Astro",
        }
    }
}

impl std::fmt::Display for Language {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.name())
    }
}
