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

#[derive(Serialize, Deserialize, sqlx::Type, Clone, Debug, PartialEq, Eq)]
#[sqlx(type_name = "text", rename_all = "lowercase")]
#[serde(rename_all = "lowercase")]
pub enum MapRole {
  Owner,
  Editor,
  Viewer,
}

impl MapRole {
  /// Returns true if this role can mutate map content (paint, tokens, colors).
  pub fn can_edit(&self) -> bool {
    matches!(self, MapRole::Owner | MapRole::Editor)
  }

  /// Returns true if this role can manage permissions and delete the map.
  pub fn is_owner(&self) -> bool {
    matches!(self, MapRole::Owner)
  }
}

/// A permission record with the associated user's username included (for display).
#[derive(Serialize, sqlx::FromRow, Clone, Debug)]
pub struct PermissionWithUser {
  pub map_id: String,
  pub user_id: Uuid,
  pub username: String,
  pub role: MapRole,
}

/// Core map record as stored/returned by create, get, and update endpoints.
#[derive(Serialize, Deserialize, sqlx::FromRow, Clone, Debug)]
pub struct GridMap {
  pub id: String,
  pub name: String,
  /// One of: "private", "public_view", "public_edit"
  pub public_access: String,
  pub owner_id: Uuid,
  pub colors: Vec<String>,
  pub created_at: DateTime<Utc>,
  pub updated_at: DateTime<Utc>,
}

/// Extended map record returned by the list endpoint.
/// Includes the owner's username, the caller's role (if any), and a
/// favorited flag.
#[derive(Serialize, sqlx::FromRow, Clone, Debug)]
pub struct ListedMap {
  pub id: String,
  pub name: String,
  pub public_access: String,
  pub owner_id: Uuid,
  pub owner_username: String,
  pub colors: Vec<String>,
  pub created_at: DateTime<Utc>,
  pub updated_at: DateTime<Utc>,
  /// The authenticated caller's role on this map, or NULL if they only have it
  /// via a favorite (no explicit permission).
  pub user_role: Option<MapRole>,
  pub is_favorited: bool,
}

#[derive(Deserialize, Clone, Debug)]
pub struct CreateMapPayload {
  pub name: String,
  /// Defaults to "private" when omitted.
  #[serde(default = "default_private")]
  pub public_access: String,
}

fn default_private() -> String {
  "private".to_string()
}

#[derive(Deserialize, Clone, Debug)]
pub struct UpdateMapPayload {
  pub name: Option<String>,
  pub public_access: Option<String>,
}

#[derive(Deserialize, Clone, Debug)]
pub struct UpdatePermissionPayload {
  /// Username of the target user (looked up server-side).
  pub username: String,
  /// New role to assign. `null` removes the permission entry.
  pub role: Option<MapRole>,
}

/// An access-request row joined with the requesting user's username.
#[derive(Serialize, sqlx::FromRow, Clone, Debug)]
pub struct AccessRequestWithUser {
  pub id: Uuid,
  pub map_id: String,
  pub user_id: Uuid,
  pub username: String,
  pub requested_role: MapRole,
  pub status: String,
  pub created_at: DateTime<Utc>,
  pub updated_at: DateTime<Utc>,
}

#[derive(Deserialize, Clone, Debug)]
pub struct CreateAccessRequestPayload {
  pub role: MapRole,
}

#[derive(Deserialize, Clone, Debug)]
pub struct ResolveAccessRequestPayload {
  /// "approve" or "deny"
  pub action: String,
}

#[derive(Serialize, Deserialize, sqlx::FromRow, Clone, Debug)]
pub struct GridCell {
  pub map_id: String,
  pub x: i32,
  pub y: i32,
  pub color: String,
}

/// Lightweight cell used for batch operations.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CellPatch {
  pub x: i32,
  pub y: i32,
  pub color: String,
}

#[derive(Serialize, Deserialize, sqlx::FromRow, Clone, Debug)]
pub struct GridToken {
  pub id: String,
  pub map_id: String,
  pub x: i32,
  pub y: i32,
  pub label: String,
  pub color: String,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct MapState {
  pub map: GridMap,
  pub cells: Vec<GridCell>,
  pub tokens: Vec<GridToken>,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ClientMessage {
  PaintCell {
    x: i32,
    y: i32,
    color: String,
  },
  PaintCells {
    cells: Vec<CellPatch>,
  },
  EraseCell {
    x: i32,
    y: i32,
  },
  AddToken {
    x: i32,
    y: i32,
    label: String,
    color: String,
  },
  MoveToken {
    id: String,
    x: i32,
    y: i32,
  },
  DeleteToken {
    id: String,
  },
  UpdateColors {
    colors: Vec<String>,
  },
}

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ServerMessage {
  State {
    cells: Vec<GridCell>,
    tokens: Vec<GridToken>,
    colors: Vec<String>,
  },
  CellPainted {
    x: i32,
    y: i32,
    color: String,
  },
  CellsBatchPainted {
    cells: Vec<CellPatch>,
  },
  CellErased {
    x: i32,
    y: i32,
  },
  TokenAdded {
    id: String,
    x: i32,
    y: i32,
    label: String,
    color: String,
  },
  TokenMoved {
    id: String,
    x: i32,
    y: i32,
  },
  TokenDeleted {
    id: String,
  },
  ColorsUpdated {
    colors: Vec<String>,
  },
  Error {
    message: String,
  },
}
