use rusqlite::Connection;

use crate::core::error::{LoreError, Result};
use crate::core::time::iso_to_ms_strict;
use crate::gitlab::types::{GitLabLabelEvent, GitLabMilestoneEvent, GitLabStateEvent};

pub fn upsert_state_events(
    conn: &Connection,
    project_id: i64,
    entity_type: &str,
    entity_local_id: i64,
    events: &[GitLabStateEvent],
) -> Result<usize> {
    let (issue_id, merge_request_id) = resolve_entity_ids(entity_type, entity_local_id)?;

    let mut stmt = conn.prepare_cached(
        "INSERT OR REPLACE INTO resource_state_events
         (gitlab_id, project_id, issue_id, merge_request_id, state,
          actor_gitlab_id, actor_username, created_at,
          source_commit, source_merge_request_iid)
         VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)",
    )?;

    let mut count = 0;
    for event in events {
        let created_at = iso_to_ms_strict(&event.created_at).map_err(LoreError::Other)?;
        let actor_id = event.user.as_ref().map(|u| u.id);
        let actor_username = event.user.as_ref().map(|u| u.username.as_str());
        let source_mr_iid = event.source_merge_request.as_ref().map(|mr| mr.iid);

        stmt.execute(rusqlite::params![
            event.id,
            project_id,
            issue_id,
            merge_request_id,
            event.state,
            actor_id,
            actor_username,
            created_at,
            event.source_commit,
            source_mr_iid,
        ])?;
        count += 1;
    }

    Ok(count)
}

pub fn upsert_label_events(
    conn: &Connection,
    project_id: i64,
    entity_type: &str,
    entity_local_id: i64,
    events: &[GitLabLabelEvent],
) -> Result<usize> {
    let (issue_id, merge_request_id) = resolve_entity_ids(entity_type, entity_local_id)?;

    let mut stmt = conn.prepare_cached(
        "INSERT OR REPLACE INTO resource_label_events
         (gitlab_id, project_id, issue_id, merge_request_id, action,
          label_name, actor_gitlab_id, actor_username, created_at)
         VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
    )?;

    let mut count = 0;
    for event in events {
        let created_at = iso_to_ms_strict(&event.created_at).map_err(LoreError::Other)?;
        let actor_id = event.user.as_ref().map(|u| u.id);
        let actor_username = event.user.as_ref().map(|u| u.username.as_str());

        stmt.execute(rusqlite::params![
            event.id,
            project_id,
            issue_id,
            merge_request_id,
            event.action,
            event.label.as_ref().map(|l| l.name.as_str()),
            actor_id,
            actor_username,
            created_at,
        ])?;
        count += 1;
    }

    Ok(count)
}

pub fn upsert_milestone_events(
    conn: &Connection,
    project_id: i64,
    entity_type: &str,
    entity_local_id: i64,
    events: &[GitLabMilestoneEvent],
) -> Result<usize> {
    let (issue_id, merge_request_id) = resolve_entity_ids(entity_type, entity_local_id)?;

    let mut stmt = conn.prepare_cached(
        "INSERT OR REPLACE INTO resource_milestone_events
         (gitlab_id, project_id, issue_id, merge_request_id, action,
          milestone_title, milestone_id, actor_gitlab_id, actor_username, created_at)
         VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)",
    )?;

    let mut count = 0;
    for event in events {
        let created_at = iso_to_ms_strict(&event.created_at).map_err(LoreError::Other)?;
        let actor_id = event.user.as_ref().map(|u| u.id);
        let actor_username = event.user.as_ref().map(|u| u.username.as_str());

        stmt.execute(rusqlite::params![
            event.id,
            project_id,
            issue_id,
            merge_request_id,
            event.action,
            event.milestone.as_ref().map(|m| m.title.as_str()),
            event.milestone.as_ref().map(|m| m.id),
            actor_id,
            actor_username,
            created_at,
        ])?;
        count += 1;
    }

    Ok(count)
}

fn resolve_entity_ids(
    entity_type: &str,
    entity_local_id: i64,
) -> Result<(Option<i64>, Option<i64>)> {
    match entity_type {
        "issue" => Ok((Some(entity_local_id), None)),
        "merge_request" => Ok((None, Some(entity_local_id))),
        _ => Err(LoreError::Other(format!(
            "Invalid entity type for resource events: {entity_type}"
        ))),
    }
}

pub fn count_events(conn: &Connection) -> Result<EventCounts> {
    let mut counts = EventCounts::default();

    let row: (i64, i64) = conn.query_row(
        "SELECT
                COUNT(CASE WHEN issue_id IS NOT NULL THEN 1 END),
                COUNT(CASE WHEN merge_request_id IS NOT NULL THEN 1 END)
             FROM resource_state_events",
        [],
        |row| Ok((row.get(0)?, row.get(1)?)),
    )?;
    counts.state_issue = row.0 as usize;
    counts.state_mr = row.1 as usize;

    let row: (i64, i64) = conn.query_row(
        "SELECT
                COUNT(CASE WHEN issue_id IS NOT NULL THEN 1 END),
                COUNT(CASE WHEN merge_request_id IS NOT NULL THEN 1 END)
             FROM resource_label_events",
        [],
        |row| Ok((row.get(0)?, row.get(1)?)),
    )?;
    counts.label_issue = row.0 as usize;
    counts.label_mr = row.1 as usize;

    let row: (i64, i64) = conn.query_row(
        "SELECT
                COUNT(CASE WHEN issue_id IS NOT NULL THEN 1 END),
                COUNT(CASE WHEN merge_request_id IS NOT NULL THEN 1 END)
             FROM resource_milestone_events",
        [],
        |row| Ok((row.get(0)?, row.get(1)?)),
    )?;
    counts.milestone_issue = row.0 as usize;
    counts.milestone_mr = row.1 as usize;

    Ok(counts)
}

#[derive(Debug, Default)]
pub struct EventCounts {
    pub state_issue: usize,
    pub state_mr: usize,
    pub label_issue: usize,
    pub label_mr: usize,
    pub milestone_issue: usize,
    pub milestone_mr: usize,
}

impl EventCounts {
    pub fn total(&self) -> usize {
        self.state_issue
            + self.state_mr
            + self.label_issue
            + self.label_mr
            + self.milestone_issue
            + self.milestone_mr
    }
}
