use std::sync::{Arc, OnceLock};

use anyhow::Result;
use async_trait::async_trait;
use regex::Regex;

use crate::reader::adapters::outbound::FileSystemReader;
use crate::reader::ports::outbound::ContractReaderPort;

static BLOCK_COMMENT_RE: OnceLock<Regex> = OnceLock::new();
static LINE_COMMENT_RE: OnceLock<Regex> = OnceLock::new();

fn block_comment_re() -> &'static Regex {
    BLOCK_COMMENT_RE.get_or_init(|| Regex::new(r"(?s)/\*.*?\*/").unwrap())
}

fn line_comment_re() -> &'static Regex {
    LINE_COMMENT_RE.get_or_init(|| Regex::new(r"//.*").unwrap())
}

pub struct SolidityContractReader {
    reader: Arc<FileSystemReader>,
}

impl SolidityContractReader {
    pub fn new(reader: Arc<FileSystemReader>) -> Self {
        Self { reader }
    }
}

#[async_trait]
impl ContractReaderPort for SolidityContractReader {
    async fn get_contract_context(&self, path: &str, include_comments: bool) -> Result<String> {
        let content = self.reader.read_file(path).await?;

        let filtered: Vec<&str> = content
            .lines()
            .filter(|l| !l.trim().starts_with("pragma"))
            .collect();
        let mut source = filtered.join("\n");

        if !include_comments {
            source = block_comment_re().replace_all(&source, "").to_string();
            source = line_comment_re().replace_all(&source, "").to_string();

            source = source
                .lines()
                .filter(|l| !l.trim().is_empty())
                .collect::<Vec<&str>>()
                .join("\n");
        }

        Ok(source)
    }
}
