// TODO: generate the enums in their own impl blocks

use jiff::Timestamp;
use l10n_embed::Localize;
use l10n_embed_derive::localize;
use pijul_extension::FileSystemRepository;
use pijul_extension::file_system::changes::CreditSource;
use pijul_extension::file_system::open_file::OpenFile;

use crate::vscode_sys;

#[localize("l10n/**/inline_credit/line_annotation.ftl")]
enum AuthorKind<'change_header> {
    Local,
    Remote {
        username: &'change_header str,
    },
    Unknown {
        public_key_signature: &'change_header str,
    },
}

#[localize("l10n/**/inline_credit/line_annotation.ftl")]
enum LineAnnotation<'change_header> {
    Tracked {
        authors: l10n_embed::list::AndList<AuthorKind<'change_header>>,
        timestamp: Timestamp,
        message: String,
    },
    Untracked {
        timestamp: Timestamp,
    },
}

pub fn range<'env>(
    env: &'env napi::Env,
    open_file: &OpenFile,
    line: usize,
) -> Result<vscode_sys::Range<'env>, napi::Error> {
    let current_line_contents = open_file
        .contents
        .text
        .get_line(line, ropey::LineType::LF_CR)
        .ok_or_else(|| napi::Error::from_reason(format!("no contents for line {line}")))?;
    let final_character_offset = current_line_contents.len() as u32;

    let start = vscode_sys::Position::new(env, line as u32, final_character_offset)?;
    let end = vscode_sys::Position::new(env, line as u32, final_character_offset)?;

    vscode_sys::Range::new(env, &start, &end)
}

pub fn render(
    localization_context: &l10n_embed::Context,
    repository: &FileSystemRepository,
    text_document: &vscode_sys::TextDocument,
    open_file: &OpenFile,
    credit_source: &CreditSource,
) -> Result<String, napi::Error> {
    let annotation = match credit_source {
        CreditSource::Tracked { vertex } => {
            let change = repository.get_change(vertex.change).map_err(|error| {
                napi::Error::from_reason(format!(
                    "Unable to get change for vertex {vertex:#?}: {error}"
                ))
            })?;

            let authors = repository
                .authors_for_change(&change.header)
                .into_iter()
                .map(|author_source| match author_source {
                    pijul_extension::author::AuthorSource::Local(_identity) => AuthorKind::Local,
                    pijul_extension::author::AuthorSource::Remote(identity) => AuthorKind::Remote {
                        username: &identity.config.author.username,
                    },
                    pijul_extension::author::AuthorSource::Unknown(public_key_signature) => {
                        AuthorKind::Unknown {
                            public_key_signature,
                        }
                    }
                })
                .collect();

            LineAnnotation::Tracked {
                authors: l10n_embed::list::AndList::new(authors),
                timestamp: change.header.timestamp,
                message: change.hashed.header.message,
            }
        }
        CreditSource::Untracked { .. } => {
            let timestamp = if text_document.get_is_dirty()? {
                Timestamp::now()
            } else {
                open_file.contents.modified_time
            };

            LineAnnotation::Untracked { timestamp }
        }
    };

    let mut localized_annotation = String::new();
    annotation.localize(localization_context, &mut localized_annotation);

    Ok(localized_annotation)
}