# Copyright (c) 2023-present Pi Dash Software, Inc. and contributors
# SPDX-License-Identifier: AGPL-3.0-only
# See the LICENSE file for details.

# Third party imports
import random
from rest_framework import serializers
from django.db import transaction


# Python imports
import re

# Module imports
from pi_dash.db.models import Project, ProjectIdentifier, WorkspaceMember, State, Estimate

from pi_dash.utils.content_validator import (
    validate_html_content,
)
from .base import BaseSerializer


class ProjectCreateSerializer(BaseSerializer):
    """
    Serializer for creating projects with workspace validation.

    Handles project creation including identifier validation, member verification,
    and workspace association for new project initialization.
    """

    PROJECT_ICON_DEFAULT_COLORS = [
        "#95999f",
        "#6d7b8a",
        "#5e6ad2",
        "#02b5ed",
        "#02b55c",
        "#f2be02",
        "#e57a00",
        "#f38e82",
    ]
    PROJECT_ICON_DEFAULT_ICONS = [
        "home",
        "apps",
        "settings",
        "star",
        "favorite",
        "done",
        "check_circle",
        "add_task",
        "create_new_folder",
        "dataset",
        "terminal",
        "key",
        "rocket",
        "public",
        "quiz",
        "mood",
        "gavel",
        "eco",
        "diamond",
        "forest",
        "bolt",
        "sync",
        "cached",
        "library_add",
        "view_timeline",
        "view_kanban",
        "empty_dashboard",
        "cycle",
    ]

    class Meta:
        model = Project
        fields = [
            "name",
            "description",
            "project_lead",
            "default_assignee",
            "identifier",
            "icon_prop",
            "emoji",
            "cover_image",
            "module_view",
            "cycle_view",
            "issue_views_view",
            "page_view",
            "intake_view",
            "guest_view_all_features",
            "members_can_edit_states",
            "archive_in",
            "close_in",
            "timezone",
            "external_source",
            "external_id",
            "is_issue_type_enabled",
            "is_time_tracking_enabled",
            "is_default",
            "repo_url",
            "base_branch",
        ]

        read_only_fields = [
            "id",
            "workspace",
            "created_at",
            "updated_at",
            "created_by",
            "updated_by",
            "logo_props",
        ]

    def validate(self, data):
        project_name = data.get("name", None)
        project_identifier = data.get("identifier", None)

        if project_name is not None and re.match(Project.FORBIDDEN_IDENTIFIER_CHARS_PATTERN, project_name):
            raise serializers.ValidationError("Project name cannot contain special characters.")

        if project_identifier is not None and re.match(Project.FORBIDDEN_IDENTIFIER_CHARS_PATTERN, project_identifier):
            raise serializers.ValidationError("Project identifier cannot contain special characters.")

        if data.get("project_lead", None) is not None:
            # Check if the project lead is a member of the workspace
            if not WorkspaceMember.objects.filter(
                workspace_id=self.context["workspace_id"],
                member_id=data.get("project_lead"),
            ).exists():
                raise serializers.ValidationError("Project lead should be a user in the workspace")

        if data.get("default_assignee", None) is not None:
            # Check if the default assignee is a member of the workspace
            if not WorkspaceMember.objects.filter(
                workspace_id=self.context["workspace_id"],
                member_id=data.get("default_assignee"),
            ).exists():
                raise serializers.ValidationError("Default assignee should be a user in the workspace")

        return data

    def create(self, validated_data):
        identifier = validated_data.get("identifier", "").strip().upper()

        if identifier == "":
            raise serializers.ValidationError(detail="Project Identifier is required")

        if ProjectIdentifier.objects.filter(name=identifier, workspace_id=self.context["workspace_id"]).exists():
            raise serializers.ValidationError(detail="Project Identifier is taken")

        if validated_data.get("logo_props", None) is None:
            # Generate a random icon and color for the project icon
            validated_data["logo_props"] = {
                "in_use": "icon",
                "icon": {
                    "name": random.choice(self.PROJECT_ICON_DEFAULT_ICONS),
                    "color": random.choice(self.PROJECT_ICON_DEFAULT_COLORS),
                },
            }

        with transaction.atomic():
            if validated_data.get("is_default") is True:
                Project.objects.filter(workspace_id=self.context["workspace_id"], is_default=True).update(
                    is_default=False
                )

            project = Project.objects.create(**validated_data, workspace_id=self.context["workspace_id"])
        return project


class ProjectUpdateSerializer(ProjectCreateSerializer):
    """
    Serializer for updating projects with enhanced state and estimation management.

    Extends project creation with update-specific validations including default state
    assignment, estimation configuration, and project setting modifications.
    """

    class Meta(ProjectCreateSerializer.Meta):
        model = Project
        fields = ProjectCreateSerializer.Meta.fields + [
            "default_state",
            "estimate",
        ]

        read_only_fields = ProjectCreateSerializer.Meta.read_only_fields

    def update(self, instance, validated_data):
        project_name = validated_data.get("name", None)
        project_identifier = validated_data.get("identifier", None)

        if project_name is not None and re.match(Project.FORBIDDEN_IDENTIFIER_CHARS_PATTERN, project_name):
            raise serializers.ValidationError("Project name cannot contain special characters.")

        if project_identifier is not None and re.match(Project.FORBIDDEN_IDENTIFIER_CHARS_PATTERN, project_identifier):
            raise serializers.ValidationError("Project identifier cannot contain special characters.")

        """Update a project"""
        if (
            validated_data.get("default_state", None) is not None
            and not State.objects.filter(project=instance, id=validated_data.get("default_state")).exists()
        ):
            # Check if the default state is a state in the project
            raise serializers.ValidationError("Default state should be a state in the project")

        if (
            validated_data.get("estimate", None) is not None
            and not Estimate.objects.filter(project=instance, id=validated_data.get("estimate").id).exists()
        ):
            # Check if the estimate is a estimate in the project
            raise serializers.ValidationError("Estimate should be a estimate in the project")
        if instance.is_default and validated_data.get("is_default") is False:
            raise serializers.ValidationError(
                "Default project cannot be unset without assigning another default project."
            )
        if validated_data.get("is_default") is True:
            with transaction.atomic():
                Project.objects.filter(workspace=instance.workspace, is_default=True).exclude(pk=instance.pk).update(
                    is_default=False
                )
                return super().update(instance, validated_data)
        return super().update(instance, validated_data)


class ProjectSerializer(BaseSerializer):
    """
    Comprehensive project serializer with metrics and member context.

    Provides complete project data including member counts, cycle/module totals,
    deployment status, and user-specific context for project management.
    """

    total_members = serializers.IntegerField(read_only=True)
    total_cycles = serializers.IntegerField(read_only=True)
    total_modules = serializers.IntegerField(read_only=True)
    is_member = serializers.BooleanField(read_only=True)
    sort_order = serializers.FloatField(read_only=True)
    member_role = serializers.IntegerField(read_only=True)
    is_deployed = serializers.BooleanField(read_only=True)
    cover_image_url = serializers.CharField(read_only=True)

    class Meta:
        model = Project
        fields = "__all__"
        read_only_fields = [
            "id",
            "emoji",
            "workspace",
            "created_at",
            "updated_at",
            "created_by",
            "updated_by",
            "deleted_at",
            "cover_image_url",
        ]

    def validate(self, data):
        project_name = data.get("name", None)
        project_identifier = data.get("identifier", None)

        if project_name is not None and re.match(Project.FORBIDDEN_IDENTIFIER_CHARS_PATTERN, project_name):
            raise serializers.ValidationError("Project name cannot contain special characters.")

        if project_identifier is not None and re.match(Project.FORBIDDEN_IDENTIFIER_CHARS_PATTERN, project_identifier):
            raise serializers.ValidationError("Project identifier cannot contain special characters.")

        if self.instance and self.instance.is_default and data.get("is_default") is False:
            raise serializers.ValidationError(
                "Default project cannot be unset without assigning another default project."
            )

        # Check project lead should be a member of the workspace
        if (
            data.get("project_lead", None) is not None
            and not WorkspaceMember.objects.filter(
                workspace_id=self.context["workspace_id"],
                member_id=data.get("project_lead"),
            ).exists()
        ):
            raise serializers.ValidationError("Project lead should be a user in the workspace")

        # Check default assignee should be a member of the workspace
        if (
            data.get("default_assignee", None) is not None
            and not WorkspaceMember.objects.filter(
                workspace_id=self.context["workspace_id"],
                member_id=data.get("default_assignee"),
            ).exists()
        ):
            raise serializers.ValidationError("Default assignee should be a user in the workspace")

        # Validate description content for security
        if "description_html" in data and data["description_html"]:
            if isinstance(data["description_html"], dict):
                is_valid, error_msg, sanitized_html = validate_html_content(str(data["description_html"]))
                # Update the data with sanitized HTML if available
                if sanitized_html is not None:
                    data["description_html"] = sanitized_html
            if not is_valid:
                raise serializers.ValidationError({"error": "html content is not valid"})

        return data

    def create(self, validated_data):
        identifier = validated_data.get("identifier", "").strip().upper()
        if identifier == "":
            raise serializers.ValidationError(detail="Project Identifier is required")

        if ProjectIdentifier.objects.filter(name=identifier, workspace_id=self.context["workspace_id"]).exists():
            raise serializers.ValidationError(detail="Project Identifier is taken")

        with transaction.atomic():
            if validated_data.get("is_default") is True:
                Project.objects.filter(workspace_id=self.context["workspace_id"], is_default=True).update(
                    is_default=False
                )

            project = Project.objects.create(**validated_data, workspace_id=self.context["workspace_id"])
            _ = ProjectIdentifier.objects.create(
                name=project.identifier,
                project=project,
                workspace_id=self.context["workspace_id"],
            )
        return project


class ProjectLiteSerializer(BaseSerializer):
    """
    Lightweight project serializer for minimal data transfer.

    Provides essential project information including identifiers, visual properties,
    and basic metadata optimized for list views and references.
    """

    cover_image_url = serializers.CharField(read_only=True)

    class Meta:
        model = Project
        fields = [
            "id",
            "identifier",
            "name",
            "cover_image",
            "icon_prop",
            "emoji",
            "description",
            "is_default",
            "cover_image_url",
        ]
        read_only_fields = fields
