use chrono::{DateTime, Utc};
use diesel::prelude::*;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::core::shared::schema::dashboards::{
    conversational_queries, dashboard_data_sources, dashboard_filters, dashboard_widgets,
    dashboards,
};

use super::types::{
    Dashboard, DashboardFilter, DashboardFilterType, DashboardLayout, DataSource,
    DataSourceConnection, DataSourceSchema, DataSourceStatus, DataSourceType, DataQuery,
    FilterOption, Widget, WidgetConfig, WidgetPosition, WidgetStyle, WidgetType,
};

#[derive(Debug, Clone, Queryable, Insertable, AsChangeset, Serialize, Deserialize)]
#[diesel(table_name = dashboards)]
pub struct DbDashboard {
    pub id: Uuid,
    pub org_id: Uuid,
    pub bot_id: Uuid,
    pub owner_id: Uuid,
    pub name: String,
    pub description: Option<String>,
    pub layout: serde_json::Value,
    pub refresh_interval: Option<i32>,
    pub is_public: bool,
    pub is_template: bool,
    pub tags: Vec<String>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Queryable, Insertable, AsChangeset, Serialize, Deserialize)]
#[diesel(table_name = dashboard_widgets)]
pub struct DbWidget {
    pub id: Uuid,
    pub dashboard_id: Uuid,
    pub widget_type: String,
    pub title: String,
    pub position_x: i32,
    pub position_y: i32,
    pub width: i32,
    pub height: i32,
    pub config: serde_json::Value,
    pub data_query: Option<serde_json::Value>,
    pub style: serde_json::Value,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Queryable, Insertable, AsChangeset, Serialize, Deserialize)]
#[diesel(table_name = dashboard_data_sources)]
pub struct DbDataSource {
    pub id: Uuid,
    pub org_id: Uuid,
    pub bot_id: Uuid,
    pub name: String,
    pub description: Option<String>,
    pub source_type: String,
    pub connection: serde_json::Value,
    pub schema_definition: serde_json::Value,
    pub refresh_schedule: Option<String>,
    pub last_sync: Option<DateTime<Utc>>,
    pub status: String,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Queryable, Insertable, Serialize, Deserialize)]
#[diesel(table_name = dashboard_filters)]
pub struct DbFilter {
    pub id: Uuid,
    pub dashboard_id: Uuid,
    pub name: String,
    pub field: String,
    pub filter_type: String,
    pub default_value: Option<serde_json::Value>,
    pub options: serde_json::Value,
    pub linked_widgets: serde_json::Value,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Queryable, Insertable, Serialize, Deserialize)]
#[diesel(table_name = conversational_queries)]
pub struct DbConversationalQuery {
    pub id: Uuid,
    pub org_id: Uuid,
    pub bot_id: Uuid,
    pub dashboard_id: Option<Uuid>,
    pub user_id: Uuid,
    pub natural_language: String,
    pub generated_query: Option<String>,
    pub result_widget_config: Option<serde_json::Value>,
    pub created_at: DateTime<Utc>,
}

pub fn db_dashboard_to_dashboard(
    db: DbDashboard,
    widgets: Vec<Widget>,
    filters: Vec<DashboardFilter>,
) -> Dashboard {
    let layout: DashboardLayout = serde_json::from_value(db.layout).unwrap_or_default();

    Dashboard {
        id: db.id,
        organization_id: db.org_id,
        owner_id: db.owner_id,
        name: db.name,
        description: db.description,
        layout,
        widgets,
        data_sources: vec![],
        filters,
        refresh_interval: db.refresh_interval,
        is_public: db.is_public,
        is_template: db.is_template,
        tags: db.tags,
        created_at: db.created_at,
        updated_at: db.updated_at,
    }
}

pub fn db_widget_to_widget(db: DbWidget) -> Widget {
    let widget_type: WidgetType = db.widget_type.parse().unwrap_or(WidgetType::Text);
    let config: WidgetConfig = serde_json::from_value(db.config).unwrap_or_default();
    let data_query: Option<DataQuery> = db.data_query.and_then(|v| serde_json::from_value(v).ok());
    let style: Option<WidgetStyle> = serde_json::from_value(db.style).ok();

    Widget {
        id: db.id,
        widget_type,
        title: db.title,
        position: WidgetPosition {
            x: db.position_x,
            y: db.position_y,
            width: db.width,
            height: db.height,
        },
        config,
        data_query,
        style,
    }
}

pub fn db_filter_to_filter(db: DbFilter) -> DashboardFilter {
    let filter_type: DashboardFilterType = db
        .filter_type
        .parse()
        .unwrap_or(DashboardFilterType::Text);
    let options: Vec<FilterOption> = serde_json::from_value(db.options).unwrap_or_default();
    let linked_widgets: Vec<Uuid> = serde_json::from_value(db.linked_widgets).unwrap_or_default();

    DashboardFilter {
        id: db.id,
        name: db.name,
        field: db.field,
        filter_type,
        default_value: db.default_value,
        options,
        linked_widgets,
    }
}

pub fn db_data_source_to_data_source(db: DbDataSource) -> DataSource {
    let source_type: DataSourceType = db
        .source_type
        .parse()
        .unwrap_or(DataSourceType::InternalTables);
    let connection: DataSourceConnection =
        serde_json::from_value(db.connection).unwrap_or_default();
    let schema: Option<DataSourceSchema> = serde_json::from_value(db.schema_definition).ok();
    let status: DataSourceStatus = db.status.parse().unwrap_or(DataSourceStatus::Inactive);

    DataSource {
        id: db.id,
        organization_id: db.org_id,
        name: db.name,
        description: db.description,
        source_type,
        connection,
        schema,
        refresh_schedule: db.refresh_schedule,
        last_sync: db.last_sync,
        status,
        created_at: db.created_at,
        updated_at: db.updated_at,
    }
}
