use super::*;
use iced::widget::column;

impl App {
    pub(super) fn model_manager(&self) -> Element<'_, Message> {
        let busy = matches!(self.model_download, ModelDownload::Active(_));
        let mut artifacts = column![];
        for (index, artifact) in model::managed_artifacts(&models_path()).iter().enumerate() {
            if index > 0 {
                artifacts = artifacts.push(rule::horizontal(1));
            }
            artifacts = artifacts.push(model_artifact_row(artifact, busy));
        }

        let mut content = column![
            text("Model Manager").size(26),
            text("Download, verify, or remove locally stored model files.")
                .size(14)
                .color(muted_text()),
        ]
        .spacing(8);
        if !matches!(self.model_download, ModelDownload::Idle) {
            content = content.push(
                container(model_download_status(&self.model_download))
                    .padding(16)
                    .style(overview_style),
            );
        }
        if let Some(error) = &self.error {
            content = content.push(text(error).style(iced::widget::text::danger));
        }
        content = content.push(Space::new().height(8)).push(
            container(scrollable(artifacts).height(Length::Fill))
                .height(Length::Fill)
                .style(overview_style),
        );

        let base: Element<'_, Message> = container(content)
            .width(Length::Fill)
            .height(Length::Fill)
            .padding(28)
            .into();
        let Some(artifact) = self.pending_model_delete else {
            return base;
        };
        let confirmation = container(
            column![
                text("Delete model file?").size(22),
                text(format!(
                    "Delete {artifact}, including any resumable partial download?"
                ))
                .size(13),
                row![
                    Space::new().width(Length::Fill),
                    action_button("Cancel").on_press(Message::CancelDeleteArtifact),
                    danger_button("Delete").on_press(Message::ConfirmDeleteArtifact),
                ]
                .spacing(8),
            ]
            .spacing(14),
        )
        .padding(22)
        .width(460)
        .style(overview_style);
        stack![
            base,
            opaque(
                container(confirmation)
                    .center_x(Length::Fill)
                    .center_y(Length::Fill)
                    .style(|_| container::Style::default()
                        .background(Color::from_rgba8(0, 0, 0, 0.68)))
            )
        ]
        .into()
    }
}

fn model_artifact_row(artifact: &ManagedArtifact, busy: bool) -> Element<'static, Message> {
    let status = match artifact.state {
        ManagedArtifactState::Missing => "Not downloaded",
        ManagedArtifactState::Partial => "Partial download",
        ManagedArtifactState::NeedsVerification => "Downloaded; verification required",
        ManagedArtifactState::Ready => "Ready and verified",
    };
    let download_label = match artifact.state {
        ManagedArtifactState::Ready => "Downloaded",
        _ if artifact.stored > 0 => "Resume",
        _ => "Download",
    };
    let download = if artifact.state == ManagedArtifactState::Ready || busy {
        action_button(download_label)
    } else {
        action_button(download_label).on_press(Message::DownloadArtifact(artifact.id))
    };
    let validate = if artifact.can_validate() && !busy {
        action_button("Validate").on_press(Message::ValidateArtifact(artifact.id))
    } else {
        action_button("Validate")
    };
    let delete = if artifact.stored > 0 && !busy {
        danger_button("Delete").on_press(Message::DeleteArtifact(artifact.id))
    } else {
        danger_button("Delete")
    };

    container(
        row![
            icon(ICON_MODEL, 28),
            column![
                text(artifact.id.to_string()).size(16),
                text(status).size(13).color(muted_text()),
                text(format!(
                    "{} on disk • {} expected",
                    format_bytes(artifact.stored),
                    format_bytes(artifact.expected),
                ))
                .size(12)
                .color(muted_text()),
            ]
            .spacing(5),
            Space::new().width(Length::Fill),
            row![download, validate, delete]
                .spacing(8)
                .align_y(Alignment::Center),
        ]
        .spacing(16)
        .align_y(Alignment::Center),
    )
    .width(Length::Fill)
    .padding([18, 20])
    .into()
}

pub(super) fn download_status_bar(download: &ActiveDownload) -> Element<'_, Message> {
    let progress = &download.progress;
    let percent = progress.fraction() * 100.0;
    let measurement = if progress.verification.is_some() {
        format!(
            "{} verified of {} ({percent:.1}%)",
            format_bytes(progress.completed()),
            format_bytes(progress.active_total()),
        )
    } else {
        format!(
            "{} of {} ({percent:.1}%)",
            format_bytes(progress.completed()),
            format_bytes(progress.active_total()),
        )
    };
    let transfer = if download.bytes_per_second > 0.0 && progress.remaining() > 0 {
        format!(
            "{}/s • about {} remaining",
            format_bytes(download.bytes_per_second as u64),
            format_duration(progress.remaining() as f64 / download.bytes_per_second),
        )
    } else {
        "Calculating time remaining…".to_owned()
    };
    let stop = if download.stopping {
        danger_button("Stopping…")
    } else {
        danger_button("Stop").on_press(Message::StopModelDownload)
    };

    container(
        row![
            text(phase_text(progress.phase)).size(12),
            progress_bar(0.0..=1.0, progress.fraction())
                .length(180)
                .girth(7),
            text(measurement).size(12),
            text(transfer).size(12),
            Space::new().width(Length::Fill),
            stop,
        ]
        .spacing(12)
        .align_y(Alignment::Center),
    )
    .width(Length::Fill)
    .padding([7, 14])
    .style(sidebar_style)
    .into()
}

fn model_download_status(download: &ModelDownload) -> Element<'_, Message> {
    let (progress, heading, speed, failed) = match download {
        ModelDownload::Idle => unreachable!("idle operations are not displayed"),
        ModelDownload::Active(active) => (
            &active.progress,
            if active.stopping {
                format!("Stopping {}…", active.artifact)
            } else {
                phase_text(active.progress.phase)
            },
            active.bytes_per_second,
            false,
        ),
        ModelDownload::Complete(artifact, operation, progress) => (
            progress,
            match operation {
                ModelOperation::Download => format!("{artifact} is downloaded and verified."),
                ModelOperation::Validate => format!("{artifact} passed validation."),
            },
            0.0,
            false,
        ),
        ModelDownload::Failed(artifact, error, progress) => (
            progress,
            format!("{artifact} operation failed: {error}"),
            0.0,
            true,
        ),
    };

    let percent = progress.fraction() * 100.0;
    let measurement = if progress.verification.is_some() {
        format!(
            "{} verified of {} ({percent:.1}%) • {} remaining",
            format_bytes(progress.completed()),
            format_bytes(progress.active_total()),
            format_bytes(progress.remaining()),
        )
    } else {
        format!(
            "{} of {} ({percent:.1}%) • {} remaining",
            format_bytes(progress.completed()),
            format_bytes(progress.active_total()),
            format_bytes(progress.remaining()),
        )
    };
    let heading = if failed {
        text(heading).size(12).style(iced::widget::text::danger)
    } else {
        text(heading).size(12)
    };
    let mut heading_row = row![heading, Space::new().width(Length::Fill)]
        .align_y(Alignment::Center)
        .spacing(8);
    if let ModelDownload::Active(active) = download {
        heading_row = heading_row.push(if active.stopping {
            danger_button("Stopping…")
        } else {
            danger_button("Stop").on_press(Message::StopModelDownload)
        });
    }
    let mut status = column![
        heading_row,
        progress_bar(0.0..=1.0, progress.fraction()).girth(8),
        text(measurement).size(12),
    ]
    .spacing(6);
    if speed > 0.0 && progress.remaining() > 0 {
        status = status.push(
            text(format!(
                "{}/s • about {} remaining",
                format_bytes(speed as u64),
                format_duration(progress.remaining() as f64 / speed),
            ))
            .size(12),
        );
    }
    status.into()
}

fn phase_text(phase: DownloadPhase) -> String {
    match phase {
        DownloadPhase::Pending(artifact) => format!("Ready to download {artifact}."),
        DownloadPhase::Downloading(artifact) => format!("Downloading {artifact}…"),
        DownloadPhase::Verifying(artifact) => format!("Verifying {artifact}…"),
        DownloadPhase::Complete => "Download complete.".to_owned(),
    }
}

pub(super) fn format_bytes(bytes: u64) -> String {
    const GB: f64 = 1_000_000_000.0;
    const MB: f64 = 1_000_000.0;
    if bytes >= 1_000_000_000 {
        format!("{:.1} GB", bytes as f64 / GB)
    } else {
        format!("{:.1} MB", bytes as f64 / MB)
    }
}

pub(super) fn format_duration(seconds: f64) -> String {
    let seconds = seconds.max(0.0).round() as u64;
    let hours = seconds / 3600;
    let minutes = seconds % 3600 / 60;
    if hours > 0 {
        format!("{hours}h {minutes}m")
    } else if minutes > 0 {
        format!("{minutes}m {}s", seconds % 60)
    } else {
        format!("{seconds}s")
    }
}
