# -*- coding: utf-8 -*-
"""DokoGen — Модели данных (только 187-ФЗ, только нужные поля)"""

from dataclasses import dataclass, field, asdict
from typing import List, Dict, Any


def _clean_single_fio(value):
    """Оставить одно первое ФИО, если в строке их несколько."""
    if not value:
        return value
    value = str(value).strip()
    for sep in (',', ';', '\n', '\r'):
        if sep in value:
            value = value.split(sep)[0].strip()
            break
    words = [w for w in value.split() if w]
    if len(words) > 3:
        value = ' '.join(words[:3])
    return value.strip()


@dataclass
class CommissionMember:
    """Член комиссии по категорированию объектов КИИ"""
    role: str = "член комиссии"
    position: str = ""
    fio: str = ""


@dataclass
class InformationSystem:
    """Объект КИИ (вместо информационной системы 152-ФЗ)"""
    name: str = ""
    description: str = ""
    is_local_network: bool = False
    is_internet: bool = False
    category: str = ""            # Категория значимости объекта КИИ: 1 / 2 / 3 / без категории
    users_list: List[str] = field(default_factory=list)      # Пользователи ОКИИ
    defense_tools_list: List[str] = field(default_factory=list)  # Средства защиты
    purpose: str = ""             # Назначение объекта (п. 236)
    structure: str = ""  # Структура объекта КИИ
    software: str = ""   # Программное обеспечение

    # ---- 236 приказ ФСТЭК: сведения об объекте ----
    sphere: str = ""              # Сфера (область) деятельности
    object_type: str = ""         # Тип объекта (ИС, АСУ, ИТКС)
    architecture: str = ""        # Архитектура объекта
    typical_object: str = ""      # Наименование типового отраслевого объекта КИИ

    # Субъект (организация, эксплуатирующая объект — как в опроснике)
    subject_name: str = ""        # Наименование организации, эксплуатирующей объект
    subject_address: str = ""     # Адрес местонахождения
    subject_element: str = ""     # Элемент (компонент) объекта, который эксплуатируется лицом
    subject_inn: str = ""         # ИНН лица, эксплуатирующего объект
    subject_kpp: str = ""         # КПП его обособленных подразделений

    # Сети электросвязи
    net_category: str = ""        # Категория сети электросвязи
    net_operator: str = ""        # Оператор связи и (или) провайдер хостинга
    net_purpose: str = ""         # Цель взаимодействия с сетью электросвязи
    net_method: str = ""          # Способ взаимодействия с указанием типа доступа

    # Программно-аппаратные средства
    hw_sw: str = ""               # Наименования программно-аппаратных средств и их количество
    os_sw: str = ""               # Общесистемное ПО (ОС, средства виртуализации)
    app_sw: str = ""              # Прикладные программы

    # Балансовая стоимость объекта КИИ (опросник)
    balance_value: str = ""       # Балансовая стоимость объекта КИИ в рублях

    # Безопасность (236 приказ)
    attacker_category: str = ""   # Категория нарушителя (внешний/внутренний)
    threats: str = ""             # Основные угрозы безопасности информации (\n — разделитель)
    incidents: str = ""           # Типы компьютерных инцидентов (\n — разделитель)
    criteria_values: str = ""     # Показатели критериев значимости (\n — разделитель)
    criteria_justification: str = ""  # Обоснование полученных значений
    org_measures: str = ""        # Организационные меры (\n — разделитель)
    tech_measures: str = ""       # Технические меры (\n — разделитель)


@dataclass
class Company:
    """Организация — основной объект (187-ФЗ)"""
    # Полное название
    full_name: str = ""
    company_name_1: str = ""  # именительный
    company_name_2: str = ""  # родительный
    company_name_3: str = ""  # дательный
    company_name_4: str = ""  # винительный
    company_name_5: str = ""  # творительный
    company_name_6: str = ""  # предложный

    # Краткое название
    short_name: str = ""
    short_name_1: str = ""
    short_name_2: str = ""
    short_name_3: str = ""
    short_name_4: str = ""
    short_name_5: str = ""
    short_name_6: str = ""

    # Адрес и реквизиты
    address: str = ""
    city_name: str = ""
    inn: str = ""
    ogrn: str = ""
    ogrn_date: str = ""
    kpp: str = ""
    okved: str = ""               # Основной ОКВЭД
    okved_extra: List[str] = field(default_factory=list)  # Дополнительные ОКВЭД
    domain_name: str = ""         # Доменное имя и внешний сетевой адрес

    # Должностные лица
    chief_position: str = ""
    chief_fio: str = ""
    responsible_position: str = ""
    responsible_fio: str = ""
    admin_position: str = ""
    admin_fio: str = ""

    # Документ
    contract_number: str = ""
    contract_date: str = ""

    # Финансовые показатели организации (опросник)
    profit: str = ""            # Сумма прибыли за предыдущий год
    profit_tax: str = ""        # Сумма налога с прибыли за предыдущий год

    # Списки
    commission: List[CommissionMember] = field(default_factory=list)
    information_systems: List[InformationSystem] = field(default_factory=list)
    employees_access: List[Dict[str, str]] = field(default_factory=list)

    # Наименование структурного подразделения по безопасности
    security_department: str = ""

    def update_declensions(self, inflect_func):
        """Обновить склонения полного названия"""
        if self.full_name:
            inflected = inflect_func(self.full_name)
            self.company_name_1 = self.full_name
            self.company_name_2 = inflected.get("gent", "")
            self.company_name_3 = inflected.get("datv", "")
            self.company_name_4 = inflected.get("accs", "")
            self.company_name_5 = inflected.get("ablt", "")
            self.company_name_6 = inflected.get("loct", "")

    def update_short_declensions(self, inflect_func):
        """Обновить склонения сокращённого названия"""
        if self.short_name:
            inflected = inflect_func(self.short_name)
            self.short_name_1 = self.short_name
            self.short_name_2 = inflected.get("gent", "")
            self.short_name_3 = inflected.get("datv", "")
            self.short_name_4 = inflected.get("accs", "")
            self.short_name_5 = inflected.get("ablt", "")
            self.short_name_6 = inflected.get("loct", "")

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Company":
        d = dict(data)
        if "commission" in d and isinstance(d["commission"], list):
            _cvalid = {f.name for f in CommissionMember.__dataclass_fields__.values()}
            d["commission"] = [
                CommissionMember(**{k: v for k, v in c.items() if k in _cvalid})
                if isinstance(c, dict) else c
                for c in d["commission"]
            ]
        if "information_systems" in d and isinstance(d["information_systems"], list):
            _ivalid = {f.name for f in InformationSystem.__dataclass_fields__.values()}
            d["information_systems"] = [
                InformationSystem(**{k: v for k, v in isys.items() if k in _ivalid})
                if isinstance(isys, dict) else isys
                for isys in d["information_systems"]
            ]
        if "employees_access" in d and not isinstance(d["employees_access"], list):
            d["employees_access"] = []
        # Отбрасываем плейсхолдеры из старых опросников («Формат: Фамилия Имя Отчество») и пустые строки
        if "employees_access" in d and isinstance(d["employees_access"], list):
            clean = []
            for e in d["employees_access"]:
                if not isinstance(e, dict):
                    continue
                fio = str(e.get("fio", "")).strip()
                pos = str(e.get("position", "")).strip()
                if fio.lower().startswith("формат:") or fio.lower().startswith("пример:"):
                    continue
                if not fio and not pos:
                    continue
                clean.append(e)
            d["employees_access"] = clean
        if "okved_extra" in d and not isinstance(d.get("okved_extra"), list):
            d["okved_extra"] = []
        valid = {f.name for f in cls.__dataclass_fields__.values()}
        obj = cls(**{k: v for k, v in d.items() if k in valid})
        # Если в поле ФИО несколько человек — оставить одного (старые опросники)
        for fio_field in ('chief_fio', 'responsible_fio', 'admin_fio'):
            val = getattr(obj, fio_field, '')
            if val:
                setattr(obj, fio_field, _clean_single_fio(val))
        return obj

    def get_folder_name(self) -> str:
        if self.short_name:
            return self.short_name
        if self.full_name:
            return self.full_name[:30]
        return "Новая_организация"
