use std::sync::Arc;

use axum::extract::State;
use maud::{html, Markup, PreEscaped, DOCTYPE};

use crate::{
    clock,
    data::{self, get_weather_icon, get_weather_name, CombinedFish, Filters},
    AppState,
};

pub struct ViewData<'a> {
    pub state: State<Arc<AppState>>,
    pub fish: Vec<CombinedFish<'a>>,
    pub caught_fish: Vec<i32>,
    pub pinned_fish: Vec<i32>,
    pub acc_id: String,
    pub filters: Filters,
    pub only_list: bool,
}

pub fn layout(content: Markup) -> Markup {
    html! {
        (DOCTYPE)
        html {
            head {
                title { "Beacon" }
                meta name="viewport" content="width=device-width";
                meta charset="utf-8";
                meta name="description" content="A big fish tracker for FFXIV.";
                meta name="author" content="the insects institute";
                meta property="og:url" content="https://fish.insects.institute";
                meta property="og:title" content="Beacon";
                meta property="og:description" content="A big fish tracker for FFXIV.";
                link rel="stylesheet" href="/static/style.css";
                script src="/static/htmx.js" {}
            }

            body { (content) }
        }
    }
}

pub fn main_page(data: ViewData) -> Markup {
    let list = html! {
        h2.clock {
            "ET " (clock::get_current_eorzea_date().format("%H:%M"))
            small { "refreshes every ~10 seconds" }
        }
        (fish_list(&data))
        script src="/static/scripts/dates.js" type="text/javascript" {}
    };

    let template = html! {
        span style="display: none;" id="account-id" { (data.acc_id) }
        (header(&data))
        div id="list" hx-get="" hx-trigger="every 10s" hx-swap="innerHTML" hx-target="this" hx-on="changeDates" {
            (list)
        }

        script src="/static/scripts/save.js" type="text/javascript" {}
    };

    if data.only_list {
        list
    } else {
        layout(html! {
            main {
                (template)
            }
        })
    }
}

pub fn fish_list(data: &ViewData) -> Markup {
    html! {
        @for fish in data.fish.clone() {
            @if !fish.filtered {
                (fish_display(&fish, data, false))
                @if !fish.entry.predators.is_empty() {
                    .predators {
                        div.predators-header { small { "⇖ Requirements" } }
                        @for predator in &fish.entry.predators {
                            @if let Some(pred_fish) = data.fish.iter().find(|f| f.entry.id == predator[0]) {
                                .predators-fish {
                                    .amount { (predator[1]) }
                                    (fish_display(pred_fish, data, true))
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

pub fn fish_display(fish: &CombinedFish, data: &ViewData, is_predator: bool) -> Markup {
    html! {
        section.fish.up[fish.is_up].alwaysup[fish.is_always_up].pinned[data.pinned_fish.contains(&(fish.entry.id as i32))] {
            .title {
                @if !is_predator {
                    div {
                        @if !data.caught_fish.contains(&(fish.entry.id as i32)) {
                            form action=(format!("/{}/catch/{}", data.acc_id, fish.entry.id)) method="post" {
                                button.catch-button type="submit" { (PreEscaped("&check;")) }
                            }
                        }

                        @if !data.pinned_fish.contains(&(fish.entry.id as i32)) {
                            form action=(format!("/{}/pin/{}", data.acc_id, fish.entry.id)) method="post" {
                                button.pin-button type="submit" { (PreEscaped("&star;"))}
                            }
                        } @else {
                            form action=(format!("/{}/pin/{}/delete", data.acc_id, fish.entry.id)) method="post" {
                                button.pin-button type="submit" { (PreEscaped("&starf;"))}
                            }
                        }
                    }
                }
                div.name {
                    h3 {
                        (fish.meta.name_en)
                        span class=(format!("patch patch-{}", fish.entry.patch as u32)) { (format!("{:.1}", fish.entry.patch)) }

                        .subtitle {
                            span { "Rarity: " (format!("{:.2}", fish.rarity * 100.)) "%" }
                        }
                    }

                    @if let Some(length) = fish.entry.intuition_length {
                        .intuition {
                            img src="/static/intuition.png";
                            div { (data::display_intuition_length(length)) }
                        }
                    }
                }
            }
            .when {
                @if let Some(window) = fish.windows.first() {
                    @if fish.is_up {
                        "closes " (window.display_end_time())
                    } @else {
                        "opens " (window.display_start_time())
                    }
                    br;
                    @if fish.is_up {
                        .date data-ts=(clock::to_earth_time(window.start_time + window.duration).timestamp_millis()) {
                            .inner id=(format!("date-{}", fish.entry.id)) hx-preserve {
                                (clock::to_earth_time(window.start_time + window.duration).format("%c %Z"))
                            }
                        }

                        @if let Some(window2) = fish.windows.get(1) {
                            .date.tiny data-ts=(clock::to_earth_time(window2.start_time).timestamp_millis()) {
                                "next: "
                                .inner id=(format!("nextwindow-{}", fish.entry.id)) hx-preserve {
                                    (clock::to_earth_time(window2.start_time).format("%c %Z"))
                                }
                            }
                        }
                    } @else {
                        .date data-ts=(clock::to_earth_time(window.start_time).timestamp_millis()) {
                            .inner id=(format!("date-{}", fish.entry.id)) hx-preserve {
                                (clock::to_earth_time(window.start_time).format("%c %Z"))
                            }
                        }
                    }
                }

            }
            .how {
                @for item_id in &fish.entry.best_catch_path {
                    @if let Some(item) = data.state.data.db_data.items.get(item_id) {
                        span.catchpath title=(item.name_en) {
                            img src=(item.get_icon_url()) width="35";

                            @if let Some(hookset) = item.get_hookset(&data.state.data) {
                                img.hookset src=(hookset) width="20";
                            }

                            @if let Some(tug) = item.get_tug(&data.state.data) {
                                span.tug { (tug) }
                            }
                        }
                    }
                }
                @if let Some(hookset) = fish.entry.hookset {
                    span.catchpath {
                        img src=(hookset) width="35";

                        @if let Some(tug) = fish.entry.tug {
                            span.tug { (tug) }
                        }
                    }

                }
            }
            .meta {
                @if let Some(location_id) = fish.entry.location {
                    @if let Some(location) = data.state.data.db_data.fishing_spots.get(&location_id) {
                        div {
                            span.zone { (fish.meta.zone_en) }
                            (location.name_en)
                        }
                    }
                }

                @if fish.entry.start_hour.is_some() && fish.entry.end_hour.is_some() {
                    div {
                        @if !fish.is_always_up {
                            "ET "
                            (clock::display_eorzea_time(&clock::set_hm_from_float(&clock::get_current_eorzea_date(), fish.entry.start_hour.unwrap())))
                            "-"
                            (clock::display_eorzea_time(&clock::set_hm_from_float(&clock::get_current_eorzea_date(), fish.entry.end_hour.unwrap())))
                        } @else {
                            "always up!"
                        }
                    }
                    div {
                        @if !fish.entry.weather_set.is_empty() {
                            @if !fish.entry.previous_weather_set.is_empty() {
                                @for weather in &fish.entry.previous_weather_set {
                                    img src=(get_weather_icon(&data.state.data, weather)) width="20" title=(get_weather_name(&data.state.data, weather));
                                }

                                " ➞ "
                            }

                            @for weather in &fish.entry.weather_set {
                                img src=(get_weather_icon(&data.state.data, weather)) width="20" title=(get_weather_name(&data.state.data, weather));
                            }
                        }
                    }
                }
            }
        }
    }
}

pub fn header(data: &ViewData) -> Markup {
    html! {
        .header {
            div {}
            .side {
                .menu {
                    span { "Beacon " (env!("CARGO_PKG_VERSION")) }
                    a href="/changelog" { "Changelog" }
                    a href="https://git.insects.institute/insects/beacon" { "Source code" }
                    a href="/logout" { "Log out" }
                }
                details {
                    summary { "Filters" }
                    form {
                        fieldset {
                            @if data.filters.non_big_fish {
                                input name="big" id="big" type="checkbox" checked;
                            } @else {
                                input name="big" id="big" type="checkbox";
                            }
                            label for="big" { "Include non big fish?" }
                        }

                        fieldset {
                            @if data.filters.include_caught {
                                input name="caught" id="caught" type="checkbox" checked;
                            } @else {
                                input name="caught" id="caught" type="checkbox";
                            }
                            label for="caught" { "Include caught fish?" }
                        }

                        fieldset {
                            label for="patches" { "Filter by patch" }
                        br;
                        select name="patches" id="patches" multiple {
                            @for patch in data::PATCHES {
                                @if data.filters.patches.contains(&patch) {
                                    option value=(patch) selected { (format!("{:.1}", patch)) }
                                } @else {
                                    option value=(patch) { (format!("{:.1}", patch)) }
                                }
                            }
                        }
                        }

                        button type="submit" { "Apply" }
                    }
                }
            }
        }
    }
}

pub fn root() -> Markup {
    layout(html! {
        h1 { "Beacon" }
        p {
            "Beacon is a website to track progress for collecting " i { "big fish" } " in Final Fantasy XIV. "
            "To use it, you need an " b { "account ID" } ", which serves to remember which fish you've caught. "
            "If you have an account ID, you can enter it here, or if you don't, create a new one. Usually, your "
            "browser will remember your ID and automatically redirect you, though."
        }

        form method="post" {
            button type="submit" { "Generate a new account ID" }
        }

        p { "or" }

        form method="get" action="/to" {
            input name="id" type="text" placeholder="Enter your account ID...";
            button type="submit" { "Go to tracker" }
        }

        h2 { "What is this about?" }
        p {
            "In Final Fantasy XIV, you can catch a number of elusive and rare fish called big fish. These can be quite tricky to even find -- most of them have a specific time window and only appear during certain weather patterns. This website helps with keeping track of all of that."
        }

        h2 { "Why not use the other tracker website?" }
        p {
            "By all means! " a href="https://ff14fish.carbuncleplushy.com" { "The original tracker" } " is a great resource, and functionally superior to this website. "
            "I mostly created this because of two reasons:"
        }
        ol {
            li { "Everything is rendered on the server - meaning it doesn't eat away at your system resources (good for laptop players!)" }
            li { "Your caught fish are synchronized across devices, and data doesn't get deleted when you clear your browser data" }
        }
        p {
            "These were the two main reasons why I created this. This website couldn't exist without Carbuncle Plushy's tracker -- it uses the same data "
            "that has been painstakingly compiled by hand! If you can, please support them."
        }

        script src="/static/scripts/load.js" type="text/javascript" {}
    })
}

pub fn new_account(id: String) -> Markup {
    layout(html! {
        p {
            "Your new account ID has been created. Plase save it in a place you remember, like a password manager. If you lose it, you will be unable to "
            "access your account."
        }

        h1 id="account-id" { (id) }

        a href=(format!("/{}", id)) { "Click here to proceed to the tracker" }

        script src="/static/scripts/save.js" type="text/javascript" {}
    })
}
