import datetime
from dataclasses import dataclass

from flask import current_app as app
from flask import request
from sqlalchemy import JSON, Boolean, DateTime, ForeignKey, Integer, String
from sqlalchemy.orm import Mapped, mapped_column, relationship

from opentakserver.extensions import db, logger
from opentakserver.functions import iso8601_string_from_datetime
from opentakserver.models.MissionRole import MissionRole


@dataclass
class Mission(db.Model):
    __tablename__ = "missions"

    INVITE = "INVITE"
    DELETE = "DELETE"
    CREATE = "CREATE"

    name: Mapped[str] = mapped_column(String(255), primary_key=True)
    description: Mapped[str] = mapped_column(String(255), nullable=True)
    chat_room: Mapped[str] = mapped_column(String(255), nullable=True)
    base_layer: Mapped[str] = mapped_column(String(255), nullable=True)
    bbox: Mapped[str] = mapped_column(String(255), nullable=True)
    path: Mapped[str] = mapped_column(String(255), nullable=True)
    classification: Mapped[str] = mapped_column(String(255), nullable=True)
    tool: Mapped[str] = mapped_column(String(255), nullable=True)
    group: Mapped[str] = mapped_column(String(255), nullable=True)
    default_role: Mapped[str] = mapped_column(String(255), nullable=True)
    keywords: Mapped[JSON] = mapped_column(JSON, nullable=True)
    creator_uid: Mapped[str] = mapped_column(
        String(255), ForeignKey("euds.uid", ondelete="CASCADE"), nullable=True
    )
    create_time: Mapped[datetime] = mapped_column(DateTime, nullable=True)
    external_data: Mapped[JSON] = mapped_column(JSON, nullable=True)
    feeds: Mapped[JSON] = mapped_column(JSON, nullable=True)
    map_layers: Mapped[JSON] = mapped_column(JSON, nullable=True)
    invite_only: Mapped[bool] = mapped_column(Boolean, nullable=True)
    expiration: Mapped[int] = mapped_column(Integer, nullable=False, default=-1)
    guid: Mapped[str] = mapped_column(String(255), nullable=True)
    password_protected: Mapped[bool] = mapped_column(Boolean, nullable=True)
    password: Mapped[str] = mapped_column(String(255), nullable=True)
    invitations = relationship(
        "MissionInvitation", cascade="all, delete-orphan", back_populates="mission"
    )
    roles = relationship("MissionRole", cascade="all, delete-orphan", back_populates="mission")
    mission_changes = relationship(
        "MissionChange", cascade="all, delete-orphan", back_populates="mission", uselist=True
    )
    contents = relationship(
        "MissionContent",
        cascade="all, delete",
        secondary="mission_content_mission",
        back_populates="mission",
        uselist=True,
    )
    cots = relationship("CoT", back_populates="mission", uselist=True)
    uids = relationship("MissionUID", cascade="all, delete-orphan", back_populates="mission")
    mission_logs = relationship(
        "MissionLogEntry", cascade="all, delete-orphan", back_populates="mission"
    )
    owner = relationship("EUD", back_populates="owned_missions")
    groups = relationship(
        "Group",
        secondary="groups_missions",
        viewonly=True,
        back_populates="missions",
        cascade="all, delete",
    )

    def serialize(self):
        return {
            "description": self.description,
            "chat_room": self.chat_room,
            "base_layer": self.base_layer,
            "bbox": self.bbox,
            "path": self.path,
            "classification": self.classification,
            "tool": self.tool,
            "group": self.group or "__ANON__",
            "default_role": self.default_role,
            "keywords": self.keywords,
            "creator_uid": self.creator_uid,
            "create_time": self.create_time,
            "external_data": self.external_data,
            "feeds": self.feeds,
            "map_layers": self.map_layers,
            "invite_only": self.invite_only,
            "expiration": self.expiration,
            "guid": self.guid,
            "password_protected": self.password_protected,
            "password": self.password,
        }

    def to_json(self):
        url = request.url_root.replace("http://", "").replace("https://", "").replace("/", "")
        json = {
            "name": self.name,
            "description": self.description or "",
            "chatRoom": self.chat_room or "",
            "baseLayer": self.base_layer or "",
            "bbox": self.bbox or "",
            "path": self.path or "",
            "classification": self.classification or "",
            "tool": self.tool or "public",
            "defaultRole": self.default_role or "",
            "keywords": self.keywords if self.keywords else [],
            "creatorUid": self.creator_uid or "",
            "createTime": iso8601_string_from_datetime(self.create_time) or "",
            "externalData": self.external_data if self.external_data else [],
            "feeds": self.feeds if self.feeds else [],
            "mapLayers": self.map_layers if self.map_layers else [],
            "inviteOnly": self.invite_only if self.invite_only is not None else False,
            "expiration": self.expiration if self.expiration is not None else -1,
            "guid": self.guid or "",
            "uids": [uid.to_json() for uid in self.uids],
            "contents": [content.to_json() for content in self.contents],
            "passwordProtected": (
                self.password_protected if self.password_protected is not None else False
            ),
            "missionChanges": [mission_change.to_json() for mission_change in self.mission_changes],
            "qr_code": f"{url}:{app.config.get('OTS_SSL_STREAMING_PORT')}:ssl,{url}-{app.config.get('OTS_MARTI_HTTPS_PORT')}-ssl-{self.name},{self.name}",
            "owner": self.owner.to_json() if self.owner else None,
            "groups": [],
        }

        for group in self.groups:
            json["groups"].append({"id": group.id, "name": group.name})

        if self.default_role == MissionRole.MISSION_SUBSCRIBER or not self.default_role:
            json["defaultRole"] = MissionRole.SUBSCRIBER_ROLE

        elif self.default_role == MissionRole.MISSION_OWNER:
            json["defaultRole"] = MissionRole.OWNER_ROLE

        else:
            json["defaultRole"] = MissionRole.READ_ONLY_ROLE

        return json

    def to_marti_json(self):
        return_value = self.to_json()

        return_value["groups"] = []
        for group in self.groups:
            return_value["groups"].append(group.name)

        return return_value
