//! Activity completions repository
//!
//! Tracks which users have completed questionnaires for activities (talks,
//! workshops, presentations) and stores their responses for internal
//! questionnaires.  Methods are added to [`AuthRepository`] so the OTP
//! adapter can reach them through `svc.auth_repo` without an extra Arc.

use anyhow::Result;
use chrono::{DateTime, Utc};
use uuid::Uuid;

use crate::database::{auth::AuthRepository, connection::DatabaseParam, DatabaseType};

// ── Row types ────────────────────────────────────────────────────────────────

/// A row from the `activity_completions` table.
#[derive(Debug, Clone)]
pub struct ActivityCompletionRow {
    pub id: Uuid,
    pub user_id: Uuid,
    pub activity_id: String,
    pub completed_at: DateTime<Utc>,
    pub questionnaire_type: String,
    pub notes: Option<String>,
}

/// A row from the `activity_responses` table.
#[derive(Debug, Clone)]
pub struct ActivityResponseRow {
    pub id: Uuid,
    pub completion_id: Uuid,
    pub question_key: String,
    pub answer: String,
}

// ── AuthRepository extension ─────────────────────────────────────────────────

impl AuthRepository {
    /// Return `true` if the user has already completed the questionnaire for
    /// the given activity.
    pub async fn check_activity_completion(
        &self,
        user_id: Uuid,
        activity_id: &str,
    ) -> Result<bool> {
        let query = match self.database.database_type() {
            DatabaseType::PostgreSQL => {
                "SELECT COUNT(*) AS cnt FROM activity_completions WHERE user_id = $1 AND \
                 activity_id = $2"
            }
            DatabaseType::SQLite => {
                "SELECT COUNT(*) AS cnt FROM activity_completions WHERE user_id = ?1 AND \
                 activity_id = ?2"
            }
        };
        let rows = self
            .database
            .fetch_all(
                query,
                &[
                    DatabaseParam::Uuid(user_id),
                    DatabaseParam::String(activity_id.to_string()),
                ],
            )
            .await?;
        let count: i64 = rows
            .first()
            .and_then(|r| r.get_i64("cnt").ok())
            .unwrap_or(0);
        Ok(count > 0)
    }

    /// Record that `user_id` completed the questionnaire for `activity_id`.
    ///
    /// Silently succeeds if the completion already exists (UNIQUE constraint
    /// on `(user_id, activity_id)`).
    pub async fn record_activity_completion(
        &self,
        user_id: Uuid,
        activity_id: &str,
        questionnaire_type: &str,
    ) -> Result<Uuid> {
        let completion_id = Uuid::new_v4();
        let query = match self.database.database_type() {
            DatabaseType::PostgreSQL => {
                "INSERT INTO activity_completions (id, user_id, activity_id, questionnaire_type) \
                 VALUES ($1, $2, $3, $4) ON CONFLICT (user_id, activity_id) DO NOTHING RETURNING id"
            }
            DatabaseType::SQLite => {
                "INSERT OR IGNORE INTO activity_completions (id, user_id, activity_id, \
                 questionnaire_type) VALUES (?1, ?2, ?3, ?4)"
            }
        };

        match self.database.database_type() {
            DatabaseType::PostgreSQL => {
                let rows = self
                    .database
                    .fetch_all(
                        query,
                        &[
                            DatabaseParam::Uuid(completion_id),
                            DatabaseParam::Uuid(user_id),
                            DatabaseParam::String(activity_id.to_string()),
                            DatabaseParam::String(questionnaire_type.to_string()),
                        ],
                    )
                    .await?;
                // If a row was already there, RETURNING returns nothing — we look it up.
                if let Some(row) = rows.first() {
                    return Ok(row.get_uuid("id")?);
                }
                self.find_completion_id(user_id, activity_id).await
            }
            DatabaseType::SQLite => {
                self.database
                    .execute(
                        query,
                        &[
                            DatabaseParam::Uuid(completion_id),
                            DatabaseParam::Uuid(user_id),
                            DatabaseParam::String(activity_id.to_string()),
                            DatabaseParam::String(questionnaire_type.to_string()),
                        ],
                    )
                    .await?;
                self.find_completion_id(user_id, activity_id).await
            }
        }
    }

    /// Look up the completion UUID for an (user, activity) pair.
    async fn find_completion_id(&self, user_id: Uuid, activity_id: &str) -> Result<Uuid> {
        let query = match self.database.database_type() {
            DatabaseType::PostgreSQL => {
                "SELECT id FROM activity_completions WHERE user_id = $1 AND activity_id = $2"
            }
            DatabaseType::SQLite => {
                "SELECT id FROM activity_completions WHERE user_id = ?1 AND activity_id = ?2"
            }
        };
        let rows = self
            .database
            .fetch_all(
                query,
                &[
                    DatabaseParam::Uuid(user_id),
                    DatabaseParam::String(activity_id.to_string()),
                ],
            )
            .await?;
        rows.first()
            .ok_or_else(|| anyhow::anyhow!("completion not found after insert"))?
            .get_uuid("id")
            .map_err(Into::into)
    }

    /// Store answers for an internal questionnaire completion.
    ///
    /// Each `(question_key, answer)` pair is inserted; duplicates are ignored
    /// so this is idempotent.
    pub async fn store_activity_responses(
        &self,
        completion_id: Uuid,
        responses: &[(String, String)],
    ) -> Result<()> {
        for (question_key, answer) in responses {
            let response_id = Uuid::new_v4();
            let query = match self.database.database_type() {
                DatabaseType::PostgreSQL => {
                    "INSERT INTO activity_responses (id, completion_id, question_key, answer) \
                     VALUES ($1, $2, $3, $4) ON CONFLICT (completion_id, question_key) DO NOTHING"
                }
                DatabaseType::SQLite => {
                    "INSERT OR IGNORE INTO activity_responses (id, completion_id, question_key, \
                     answer) VALUES (?1, ?2, ?3, ?4)"
                }
            };
            self.database
                .execute(
                    query,
                    &[
                        DatabaseParam::Uuid(response_id),
                        DatabaseParam::Uuid(completion_id),
                        DatabaseParam::String(question_key.clone()),
                        DatabaseParam::String(answer.clone()),
                    ],
                )
                .await?;
        }
        Ok(())
    }

    /// Return all completions for a user, newest first.
    pub async fn get_user_completions(&self, user_id: Uuid) -> Result<Vec<ActivityCompletionRow>> {
        let query = match self.database.database_type() {
            DatabaseType::PostgreSQL => {
                "SELECT id, user_id, activity_id, completed_at, questionnaire_type, notes FROM \
                 activity_completions WHERE user_id = $1 ORDER BY completed_at DESC"
            }
            DatabaseType::SQLite => {
                "SELECT id, user_id, activity_id, completed_at, questionnaire_type, notes FROM \
                 activity_completions WHERE user_id = ?1 ORDER BY completed_at DESC"
            }
        };
        let rows = self
            .database
            .fetch_all(query, &[DatabaseParam::Uuid(user_id)])
            .await?;
        rows.iter()
            .map(|r| {
                Ok(ActivityCompletionRow {
                    id: r.get_uuid("id")?,
                    user_id: r.get_uuid("user_id")?,
                    activity_id: r.get_string("activity_id")?,
                    completed_at: r.get_datetime("completed_at")?,
                    questionnaire_type: r.get_string("questionnaire_type")?,
                    notes: r.get_optional_string("notes")?,
                })
            })
            .collect()
    }
}
