from datetime import date, timedelta
from pathlib import Path
from uuid import uuid4

from django.db import transaction

from plane.db.models import (
    DEFAULT_STATES,
    FileAsset,
    Issue,
    IssueAssignee,
    IssueView,
    Profile,
    Project,
    ProjectMember,
    State,
    User,
    Workspace,
    WorkspaceMember,
)
from plane.license.models import Instance, InstanceAdmin
from plane.settings.storage import S3Storage

PASSWORD = "NodeDC123!"
TODAY = date.today()
ATTACHMENT_PATH = Path("/tmp/nodedc-document-request-template.txt")

USERS = [
    {
        "email": "admin@nodedc.local",
        "username": "admin_nodedc",
        "first_name": "Алексей",
        "last_name": "Админ",
        "display_name": "Алексей Админ",
        "workspace_role": 20,
    },
    {
        "email": "accountant@nodedc.local",
        "username": "accountant_nodedc",
        "first_name": "Борис",
        "last_name": "Бухгалтер",
        "display_name": "Борис Бухгалтер",
        "workspace_role": 15,
    },
    {
        "email": "manager@nodedc.local",
        "username": "manager_nodedc",
        "first_name": "Марина",
        "last_name": "Менеджер",
        "display_name": "Марина Менеджер",
        "workspace_role": 15,
    },
    {
        "email": "docs@nodedc.local",
        "username": "docs_nodedc",
        "first_name": "Дарья",
        "last_name": "Документы",
        "display_name": "Дарья Документы",
        "workspace_role": 15,
    },
]

PROJECTS = [
    {
        "name": "Бухгалтерия",
        "identifier": "BUH",
        "description": "Контур для финансовых задач, сверок и согласований расходов.",
        "members": ["admin@nodedc.local", "accountant@nodedc.local"],
    },
    {
        "name": "Менеджеры",
        "identifier": "MGR",
        "description": "Контур для менеджерских задач, общих поручений и статусов.",
        "members": ["admin@nodedc.local", "manager@nodedc.local"],
    },
    {
        "name": "Запросы документов",
        "identifier": "DOC",
        "description": "Контур для входящих и исходящих запросов документов, актов и счетов.",
        "members": ["admin@nodedc.local", "docs@nodedc.local", "accountant@nodedc.local"],
    },
]

STATE_TEMPLATES = [
    {"group": "backlog", "name": "Бэклог", "color": "#60646C", "default": True},
    {"group": "unstarted", "name": "К выполнению", "color": "#60646C", "default": False},
    {"group": "started", "name": "В работе", "color": "#F59E0B", "default": False},
    {"group": "completed", "name": "Готово", "color": "#46A758", "default": False},
    {"group": "cancelled", "name": "Отменено", "color": "#9AA4BC", "default": False},
    {"group": "triage", "name": "Триаж", "color": "#4E5355", "default": False},
]

ISSUES = [
    {
        "project": "Бухгалтерия",
        "name": "Подготовить сверку с контрагентами за март",
        "description_html": "<p>Подготовить короткую сверку по основным контрагентам за март.</p><ul><li>Проверить закрывающие документы</li><li>Сверить суммы</li><li>Отметить расхождения</li></ul>",
        "priority": "high",
        "state_group": "unstarted",
        "assignees": ["accountant@nodedc.local"],
        "start_date": TODAY,
        "target_date": TODAY + timedelta(days=2),
    },
    {
        "project": "Менеджеры",
        "name": "Подготовить недельный статус по команде продаж",
        "description_html": "<p>Общая задача для контура менеджеров.</p><p>Нужно собрать краткий weekly update по активным клиентам и рискам.</p>",
        "priority": "medium",
        "state_group": "backlog",
        "assignees": [],
        "start_date": TODAY,
        "target_date": None,
    },
    {
        "project": "Запросы документов",
        "name": "Запросить закрывающие документы по договору ND-24",
        "description_html": "<p>Связаться с контрагентом и запросить закрывающие документы.</p><ul><li>Акт</li><li>Счёт</li><li>Подписанный договор</li></ul><p>Шаблон письма приложен вложением.</p>",
        "priority": "urgent",
        "state_group": "started",
        "assignees": ["docs@nodedc.local"],
        "start_date": TODAY,
        "target_date": TODAY + timedelta(days=1),
        "attachment": True,
    },
    {
        "project": "Бухгалтерия",
        "name": "Согласовать лимиты расходов на май",
        "description_html": "<p>Подготовить лимиты и согласовать их с ответственными менеджерами до конца недели.</p>",
        "priority": "high",
        "state_group": "unstarted",
        "assignees": ["admin@nodedc.local", "manager@nodedc.local"],
        "start_date": TODAY,
        "target_date": TODAY + timedelta(days=5),
    },
]


def ensure_user(spec):
    user = User.objects.filter(email=spec["email"]).first()
    if user is None:
        user = User.objects.create(email=spec["email"], username=spec["username"])
    user.username = spec["username"]
    user.first_name = spec["first_name"]
    user.last_name = spec["last_name"]
    user.display_name = spec["display_name"]
    user.is_active = True
    user.is_email_verified = True
    user.user_timezone = "Europe/Moscow"
    user.is_staff = spec["workspace_role"] == 20
    user.set_password(PASSWORD)
    user.save()

    profile, _ = Profile.objects.get_or_create(user=user)
    profile.language = "ru"
    profile.is_onboarded = True
    profile.company_name = "NodeDC"
    profile.onboarding_step = {
        "profile_complete": True,
        "workspace_create": True,
        "workspace_invite": True,
        "workspace_join": True,
    }
    profile.save()
    return user, profile


def ensure_workspace(admin_user, user_map):
    workspace, _ = Workspace.objects.get_or_create(
        slug="nodedc",
        defaults={
            "name": "NodeDC",
            "owner": admin_user,
            "timezone": "Europe/Moscow",
        },
    )
    workspace.name = "NodeDC"
    workspace.owner = admin_user
    workspace.timezone = "Europe/Moscow"
    workspace.organization_size = "11-50"
    workspace.save()

    for spec in USERS:
        member, _ = WorkspaceMember.objects.get_or_create(
            workspace=workspace,
            member=user_map[spec["email"]],
            defaults={"role": spec["workspace_role"], "company_role": spec["display_name"]},
        )
        member.role = spec["workspace_role"]
        member.company_role = spec["display_name"]
        member.is_active = True
        member.save()

    for user in user_map.values():
        profile = Profile.objects.get(user=user)
        profile.last_workspace_id = workspace.id
        profile.save(update_fields=["last_workspace_id"])

    return workspace


def ensure_project(workspace, admin_user, user_map, spec):
    project, _ = Project.objects.get_or_create(
        workspace=workspace,
        identifier=spec["identifier"],
        defaults={
            "name": spec["name"],
            "description": spec["description"],
            "network": 0,
            "project_lead": admin_user,
        },
    )
    project.name = spec["name"]
    project.description = spec["description"]
    project.network = 0
    project.project_lead = admin_user
    project.timezone = "Europe/Moscow"
    project.save()

    for idx, state_spec in enumerate(STATE_TEMPLATES):
        state = State.all_state_objects.filter(project=project, group=state_spec["group"]).first()
        if state is None:
            state = State(project=project, workspace=workspace)
        state.name = state_spec["name"]
        state.group = state_spec["group"]
        state.color = state_spec["color"]
        state.default = state_spec["default"]
        state.is_triage = state_spec["group"] == "triage"
        state.sequence = DEFAULT_STATES[idx]["sequence"]
        state.created_by = admin_user
        state.updated_by = admin_user
        state.save(disable_auto_set_user=True)
        if state_spec["default"]:
            project.default_state = state

    project.save()

    for email in spec["members"]:
        project_member, _ = ProjectMember.objects.get_or_create(
            project=project,
            member=user_map[email],
            defaults={
                "workspace": workspace,
                "role": 20 if email == "admin@nodedc.local" else 15,
            },
        )
        project_member.workspace = workspace
        project_member.role = 20 if email == "admin@nodedc.local" else 15
        project_member.is_active = True
        project_member.save()

    return project


def ensure_issue(workspace, project, admin_user, user_map, spec):
    state = State.all_state_objects.get(project=project, group=spec["state_group"])
    issue, created = Issue.objects.get_or_create(
        project=project,
        workspace=workspace,
        name=spec["name"],
        defaults={
            "state": state,
            "priority": spec["priority"],
            "start_date": spec["start_date"],
            "target_date": spec["target_date"],
            "description_html": spec["description_html"],
            "created_by": admin_user,
            "updated_by": admin_user,
        },
    )
    issue.state = state
    issue.priority = spec["priority"]
    issue.start_date = spec["start_date"]
    issue.target_date = spec["target_date"]
    issue.description_html = spec["description_html"]
    issue.created_by = admin_user
    issue.updated_by = admin_user
    issue.save(disable_auto_set_user=True)
    IssueAssignee.objects.filter(issue=issue).delete()
    for email in spec["assignees"]:
        IssueAssignee.objects.get_or_create(
            issue=issue,
            assignee=user_map[email],
            defaults={
                "project": project,
                "workspace": workspace,
                "created_by": admin_user,
                "updated_by": admin_user,
            },
        )

    legacy_attachments = issue.issue_attachment.all()
    if legacy_attachments.exists():
        legacy_attachments.delete()

    has_visible_attachment = FileAsset.objects.filter(
        issue=issue,
        workspace=workspace,
        project=project,
        entity_type=FileAsset.EntityTypeContext.ISSUE_ATTACHMENT,
        is_uploaded=True,
    ).exists()

    if spec.get("attachment") and ATTACHMENT_PATH.exists() and not has_visible_attachment:
        object_name = f"nodedc/{uuid4().hex[:12]}-doc-template.txt"
        storage = S3Storage()
        with ATTACHMENT_PATH.open("rb") as file_obj:
            uploaded = storage.upload_file(file_obj, object_name, content_type="text/plain")
        if not uploaded:
            raise RuntimeError(f"Failed to upload demo attachment to object storage: {object_name}")
        FileAsset.objects.create(
            attributes={
                "name": ATTACHMENT_PATH.name,
                "type": "text/plain",
                "size": ATTACHMENT_PATH.stat().st_size,
            },
            asset=object_name,
            size=ATTACHMENT_PATH.stat().st_size,
            user=admin_user,
            workspace=workspace,
            project=project,
            issue=issue,
            entity_type=FileAsset.EntityTypeContext.ISSUE_ATTACHMENT,
            is_uploaded=True,
            created_by=admin_user,
            updated_by=admin_user,
        )

    return issue, created


def ensure_view(workspace, project, admin_user):
    view, _ = IssueView.objects.get_or_create(
        workspace=workspace,
        project=project,
        name="Срочные документы",
        defaults={
            "description": "Быстрый просмотр срочных запросов документов",
            "filters": {"priority": ["urgent", "high"]},
            "rich_filters": {},
            "owned_by": admin_user,
            "access": 1,
            "logo_props": {},
        },
    )
    view.description = "Быстрый просмотр срочных запросов документов"
    view.filters = {"priority": ["urgent", "high"]}
    view.rich_filters = {}
    view.owned_by = admin_user
    view.access = 1
    view.save(disable_auto_set_user=True)
    return view


@transaction.atomic
def main():
    if ATTACHMENT_PATH.exists() is False:
        raise FileNotFoundError(f"Attachment file not found: {ATTACHMENT_PATH}")

    user_map = {}
    for spec in USERS:
        user, _ = ensure_user(spec)
        user_map[spec["email"]] = user

    admin_user = user_map["admin@nodedc.local"]
    instance = Instance.objects.first()
    if instance is None:
        raise RuntimeError("Plane instance is not initialized")
    instance.instance_name = "NodeDC Plane PoC"
    instance.domain = "http://localhost:8090"
    instance.is_setup_done = True
    instance.is_signup_screen_visited = True
    instance.save(update_fields=["instance_name", "domain", "is_setup_done", "is_signup_screen_visited", "updated_at"])
    InstanceAdmin.objects.get_or_create(user=admin_user, instance=instance, defaults={"role": 20, "is_verified": True})

    workspace = ensure_workspace(admin_user, user_map)
    project_map = {}
    for project_spec in PROJECTS:
        project_map[project_spec["name"]] = ensure_project(workspace, admin_user, user_map, project_spec)

    for issue_spec in ISSUES:
        ensure_issue(workspace, project_map[issue_spec["project"]], admin_user, user_map, issue_spec)

    ensure_view(workspace, project_map["Запросы документов"], admin_user)

    summary = {
        "workspace": workspace.slug,
        "users": [spec["email"] for spec in USERS],
        "projects": [spec["name"] for spec in PROJECTS],
        "issues": [spec["name"] for spec in ISSUES],
        "default_password": PASSWORD,
    }
    print(summary)


main()
