use std::{
    ops::Range,
    path::{Path, PathBuf},
};

use serde::{Deserialize, Serialize};

use crate::indexer::{IndexId, ToIndexId};

#[derive(Serialize, Deserialize, Clone)]
pub struct SourceCode {
    pub id: String,
    pub git_url: String,
    pub basedir: String,
    pub filepath: String,
    pub language: String,
    pub max_line_length: usize,
    pub avg_line_length: f32,
    pub alphanum_fraction: f32,
    pub number_fraction: f32,
    pub num_lines: usize,
    pub tags: Vec<Tag>,
}

impl ToIndexId for SourceCode {
    fn to_index_id(&self) -> IndexId {
        IndexId {
            source_id: self.git_url.clone(),
            id: self.id.clone(),
        }
    }
}

impl SourceCode {
    pub fn read_content(&self) -> std::io::Result<String> {
        let path = self.absolute_path();
        std::fs::read_to_string(path)
    }

    pub fn absolute_path(&self) -> PathBuf {
        Path::new(&self.basedir).join(&self.filepath)
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Point {
    pub row: usize,
    pub column: usize,
}

impl Point {
    pub fn new(row: usize, column: usize) -> Self {
        Self { row, column }
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Tag {
    pub range: Range<usize>,
    pub name_range: Range<usize>,
    pub utf16_column_range: Range<usize>,
    pub span: Range<Point>,
    pub line_range: Range<usize>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub docs: Option<String>,
    pub is_definition: bool,
    pub syntax_type_name: String,
}
