use sqlx::PgPool;

use crate::error::Result;
use crate::models::SanctionedCountry;

pub trait CountryRepository: Send + Sync {
    /// Fetch all sanctioned countries.
    fn list_all(
        &self,
    ) -> impl std::future::Future<Output = Result<Vec<SanctionedCountry>>> + Send;

    /// Upsert a sanctioned country (insert if not exists).
    fn upsert(
        &self,
        code: &str,
        name: &str,
        program: &str,
    ) -> impl std::future::Future<Output = Result<()>> + Send;
}

#[derive(Clone)]
pub struct PgCountryRepository {
    pool: PgPool,
}

impl PgCountryRepository {
    pub fn new(pool: PgPool) -> Self {
        Self { pool }
    }
}

impl CountryRepository for PgCountryRepository {
    async fn list_all(&self) -> Result<Vec<SanctionedCountry>> {
        let rows = sqlx::query_as::<_, (String, String, String)>(
            "SELECT code, name, program FROM sanctioned_countries ORDER BY code, program",
        )
        .fetch_all(&self.pool)
        .await?;

        Ok(rows
            .into_iter()
            .map(|(code, name, program)| SanctionedCountry {
                code,
                name,
                program,
            })
            .collect())
    }

    async fn upsert(&self, code: &str, name: &str, program: &str) -> Result<()> {
        sqlx::query(
            r#"
            INSERT INTO sanctioned_countries (code, name, program, created_at, updated_at)
            VALUES ($1, $2, $3, NOW(), NOW())
            ON CONFLICT (code, program) DO UPDATE SET
                name = EXCLUDED.name,
                updated_at = NOW()
            "#,
        )
        .bind(code)
        .bind(name)
        .bind(program)
        .execute(&self.pool)
        .await?;

        Ok(())
    }
}
