use std::{collections::HashMap, process::Command};

use crate::get_global_args;
use crate::io::read_line;
use reqwest;
use serde::Deserialize;

pub enum FallbackVersion {
    None,
    Installed,
    Latest,
}

fn get_minor_version(version: &str) -> String {
    version.split('.').take(2).collect::<Vec<&str>>().join(".")
}

pub fn get_installed_php_version() -> Option<String> {
    let output = Command::new("php").arg("-v").output().ok()?;
    let output_text = String::from_utf8(output.stdout).unwrap();

    let version = output_text.lines().next()?.split_whitespace().nth(1)?;
    let minor = get_minor_version(version);

    Some(minor)
}

#[derive(Deserialize, Debug)]
struct PHPRelease {
    version: String,
}

#[derive(Deserialize, Debug)]
struct PHPReleases {
    #[serde(flatten)]
    inner: HashMap<String, PHPRelease>,
}

pub fn get_latest_php_version() -> Option<String> {
    if get_global_args().offline {
        return None;
    }

    let res = reqwest::blocking::get("https://www.php.net/releases/index.php?json")
        .ok()?
        .json::<PHPReleases>()
        .ok()?;

    let latest = res
        .inner
        .into_iter()
        .max_by_key(|(major, _)| major.to_owned())
        .unwrap()
        .1;

    let minor = get_minor_version(&latest.version);
    Some(minor)
}

pub fn get_version(
    action: &str,
    fallback_mode: FallbackVersion,
) -> Result<Option<String>, Box<dyn std::error::Error>> {
    let default_version = match fallback_mode {
        FallbackVersion::None => None,
        FallbackVersion::Installed => get_installed_php_version(),
        FallbackVersion::Latest => get_latest_php_version(),
    };

    let prompt = match default_version.as_ref() {
        Some(version) => format!("Enter version to {} (default {}):", action, version),
        None => format!("Enter version to {}:", action),
    };

    if get_global_args().non_interactive {
        return Ok(default_version);
    }

    let response = read_line(&prompt)?;

    Ok(if response.is_empty() {
        default_version
    } else {
        Some(response)
    })
}
