use crate::error::Result;
use sqlx::{types::chrono::NaiveDateTime, FromRow, MySqlPool};

#[derive(Debug, Clone, FromRow, Default)]
pub struct PipelineReport {
    pub collection_source: String,
    pub documents_source: String,
    pub index_sink: String,
    pub cost_millis: i64,
    pub meta_version: i64,
    pub start_version: i64,
    pub end_version: i64,
    pub created_at: NaiveDateTime,
    pub updated_at: NaiveDateTime,
}

const PIPELINE_REPORT_TABLE_NAME: &str = "pipeline_report";

impl PipelineReport {
    pub fn new(
        collection_source: String,
        documents_source: String,
        index_sink: String,
        cost_millis: i64,
        meta_version: i64,
        start_version: i64,
        end_version: i64,
    ) -> Self {
        Self {
            collection_source,
            documents_source,
            index_sink,
            cost_millis,
            meta_version,
            start_version,
            end_version,
            ..Default::default()
        }
    }

    pub async fn init(db: &MySqlPool) -> Result<()> {
        let query = format!(
            r#"
        CREATE TABLE IF NOT EXISTS `{}`(
          `id` VARCHAR(36) PRIMARY KEY,
          `collection_source` JSON,
          `documents_source` JSON,
          `index_sink` JSON,
          `cost_millis` BIGINT NOT NULL,
          `meta_version` BIGINT NOT NULL,
          `start_version` BIGINT NOT NULL,
          `end_version` BIGINT NOT NULL,
          `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
          `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
        );
        "#,
            PIPELINE_REPORT_TABLE_NAME
        );
        sqlx::query(&query).execute(db).await?;
        Ok(())
    }

    pub async fn save(&self, db: &MySqlPool) -> Result<()> {
        let query = format!(
            r#"
        INSERT INTO `{}` (`id`, `collection_source`, `documents_source`, `index_sink`, `cost_millis`, `meta_version`, `start_version`, `end_version`)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?)
        "#,
            PIPELINE_REPORT_TABLE_NAME
        );
        sqlx::query(&query)
            .bind(uuid::Uuid::new_v4().to_string())
            .bind(&self.collection_source)
            .bind(&self.documents_source)
            .bind(&self.index_sink)
            .bind(self.cost_millis)
            .bind(self.meta_version)
            .bind(self.start_version)
            .bind(self.end_version)
            .execute(db)
            .await?;
        Ok(())
    }

    pub async fn find_offset(db: &MySqlPool, offset: u64, limit: u64) -> Result<Vec<Self>> {
        let query = format!(
            r#"
        SELECT 
            CAST(`collection_source` AS CHAR) AS `collection_source`, 
            CAST(`documents_source` AS CHAR) as `documents_source`, 
            CAST(`index_sink` AS CHAR) as `index_sink`, 
            `cost_millis`, 
            `created_at`, 
            `updated_at`,
            `meta_version`,
            `start_version`,
            `end_version`
        FROM `{}` ORDER BY `created_at` DESC LIMIT ?, ?
        "#,
            PIPELINE_REPORT_TABLE_NAME
        );
        let reports = sqlx::query_as::<_, PipelineReport>(&query)
            .bind(offset)
            .bind(limit)
            .fetch_all(db)
            .await?;
        Ok(reports)
    }
}
