#!/usr/bin/env python3
"""
IB System Documentation Manager
Skrypt do parsowania, modyfikowania i generowania raportów z dokumentacji YAML.
"""

import yaml
import csv
import json
import argparse
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Optional, Any
from dataclasses import dataclass, field


@dataclass
class IOPoint:
    io_id: str
    status: str
    mapping: str
    description: str
    
    def to_dict(self) -> dict:
        return {'io_id': self.io_id, 'status': self.status, 'mapping': self.mapping, 'description': self.description}


@dataclass
class Device:
    device_id: str
    model_id: str
    location: str
    description: str
    bus_id: str
    address: str
    inputs: List[IOPoint] = field(default_factory=list)
    outputs: List[IOPoint] = field(default_factory=list)
    
    def to_dict(self) -> dict:
        return {
            'device_id': self.device_id, 'model_id': self.model_id, 'location': self.location,
            'description': self.description, 'bus_id': self.bus_id, 'address': self.address,
            'inputs': [io.to_dict() for io in self.inputs],
            'outputs': [io.to_dict() for io in self.outputs]
        }


class IBDocumentManager:
    def __init__(self, yaml_path: str):
        self.yaml_path = Path(yaml_path)
        self.data: Dict = {}
        self.devices: Dict[str, Device] = {}
        self.load()
    
    def load(self) -> None:
        if not self.yaml_path.exists():
            raise FileNotFoundError(f"Plik nie istnieje: {self.yaml_path}")
        with open(self.yaml_path, 'r', encoding='utf-8') as f:
            self.data = yaml.safe_load(f)
        self._parse_devices()
        print(f"✓ Wczytano: {self.yaml_path}")
        print(f"  Projekt: {self.data['project'].get('title', 'N/A')}")
        print(f"  Urządzeń: {len(self.devices)}")
    
    def _parse_devices(self) -> None:
        self.devices.clear()
        hw_spec = self.data.get('project', {}).get('hardware_specification', {})
        for dev_data in hw_spec.get('devices', []):
            device_id = dev_data.get('device_id', '')
            connections = dev_data.get('connections', [])
            bus_id = connections[0].get('bus_id', '') if connections else ''
            address = connections[0].get('address', '') if connections else ''
            inputs, outputs = [], []
            for io_data in dev_data.get('io', []):
                io_point = IOPoint(
                    io_id=io_data.get('io_id', ''), status=io_data.get('status', ''),
                    mapping=io_data.get('mapping', ''), description=io_data.get('description', '')
                )
                if 'input' in io_point.io_id:
                    inputs.append(io_point)
                else:
                    outputs.append(io_point)
            device = Device(
                device_id=device_id, model_id=dev_data.get('model_id', ''),
                location=dev_data.get('location', ''), description=dev_data.get('description', '').strip(),
                bus_id=bus_id, address=address, inputs=inputs, outputs=outputs
            )
            self.devices[device_id] = device
    
    def save(self, output_path: Optional[str] = None) -> None:
        path = Path(output_path) if output_path else self.yaml_path
        self.data['project']['updated_at'] = datetime.now().strftime('%Y-%m-%d')
        with open(path, 'w', encoding='utf-8') as f:
            yaml.dump(self.data, f, allow_unicode=True, default_flow_style=False, sort_keys=False)
        print(f"✓ Zapisano: {path}")

    def _get_device_category(self, device: 'Device') -> str:
        desc_lower = device.description.lower()
        loc_lower = device.location.lower()
        if 'oświetleni' in desc_lower or any(x in loc_lower for x in ['wiatrołap', 'kuchnia', 'salon', 'korytarz', 'biuro', 'sypialnia', 'łazienka', 'wc', 'basia', 'poddasze', 'jadalnia', 'garderoba']):
            return 'OSWIETLENIE'
        elif 'ogrzewan' in desc_lower or 'temp' in desc_lower or 'komink' in desc_lower:
            return 'OGRZEWANIE'
        elif 'nawadnian' in desc_lower or 'zraszacz' in desc_lower or 'pompa' in desc_lower:
            return 'NAWADNIANIE'
        else:
            return 'INNE'
    
    def _sort_devices_by_id(self, devices: List['Device']) -> List['Device']:
        def get_numeric_id(dev):
            try:
                return int(dev.address.split('.')[-1])
            except:
                return 999
        return sorted(devices, key=get_numeric_id)
    
    def _get_device_stats(self, device: 'Device') -> dict:
        inputs_ok = sum(1 for io in device.inputs if io.status == 'ok')
        outputs_do_ok = sum(1 for io in device.outputs if 'do' in io.io_id and io.status == 'ok')
        total_inputs = len(device.inputs)
        total_do = sum(1 for io in device.outputs if 'do' in io.io_id)
        return {
            'inputs_ok': inputs_ok, 'total_inputs': total_inputs,
            'outputs_do_ok': outputs_do_ok, 'total_do': total_do,
            'status': 'OK' if inputs_ok == total_inputs and outputs_do_ok == total_do else '!!'
        }

    def report_stats(self) -> None:
        """Wyświetla statystyki projektu"""
        total_inputs = sum(len(d.inputs) for d in self.devices.values())
        total_outputs = sum(len(d.outputs) for d in self.devices.values())
        ok_inputs = sum(1 for d in self.devices.values() for io in d.inputs if io.status == "ok")
        ok_outputs = sum(1 for d in self.devices.values() for io in d.outputs if io.status == "ok")
        print(f"\n=== STATYSTYKI PROJEKTU ===")
        print(f"Urządzeń: {len(self.devices)}")
        print(f"Wejść: {total_inputs} (OK: {ok_inputs})")
        print(f"Wyjść: {total_outputs} (OK: {ok_outputs})")
        print(f"Razem I/O: {total_inputs + total_outputs}")

    def report_connections(self) -> str:
        lines = ["# SCHEMAT POLACZEN - PRZYCISKI -> WYJSCIA", f"Wygenerowano: {datetime.now().strftime('%Y-%m-%d %H:%M')}", "", "=" * 100]
        for device_id, device in sorted(self.devices.items()):
            lines.extend(["", f"## {device.location} ({device.device_id})", f"   Adres: {device.bus_id}.{device.address}", "-" * 80])
            for inp in device.inputs:
                if inp.io_id.startswith('input.t.'):
                    idx = inp.io_id.split('.')[-1]
                    out = next((o for o in device.outputs if o.io_id == f'output.do.{idx}'), None)
                    inp_desc = inp.description[:35] if inp.description else '-'
                    out_desc = out.description[:35] if out and out.description else '-'
                    lines.append(f"{inp.io_id:<15} [{inp.status:<4}] {inp_desc}")
                    lines.append(f"  --> output.do.{idx:<6} [{out.status if out else '-':<4}] {out_desc}")
        return "\n".join(lines)

    def report_summary_table(self) -> str:
        lines = [
            "",
            "=" * 82,
            "                        PODSUMOWANIE STEROWNIKOW                                 ",
            f"                     Wygenerowano: {datetime.now().strftime('%Y-%m-%d %H:%M')}",
            "=" * 82,
            ""
        ]
        
        categories = {}
        for device in self.devices.values():
            cat = self._get_device_category(device)
            if cat not in categories:
                categories[cat] = []
            categories[cat].append(device)
        
        cat_order = ['OSWIETLENIE', 'OGRZEWANIE', 'NAWADNIANIE', 'INNE']
        cat_icons = {'OSWIETLENIE': '[L]', 'OGRZEWANIE': '[H]', 'NAWADNIANIE': '[W]', 'INNE': '[?]'}
        
        for cat_name in cat_order:
            if cat_name not in categories:
                continue
            
            devices = self._sort_devices_by_id(categories[cat_name])
            
            lines.append(f"+--- {cat_icons[cat_name]} {cat_name} ({len(devices)} szt.) " + "-" * (55 - len(cat_name)))
            lines.append("|")
            lines.append("|  ID    | Lokalizacja                      | Wejscia | Wyjscia | Status")
            lines.append("|  ------+----------------------------------+---------+---------+-------")
            
            for device in devices:
                stats = self._get_device_stats(device)
                id_num = device.address.split('.')[-1]
                loc = device.location[:32].ljust(32)
                inp = f"{stats['inputs_ok']}/{stats['total_inputs']}"
                out = f"{stats['outputs_do_ok']}/{stats['total_do']}"
                
                lines.append(f"|  {id_num:>4}  | {loc} | {inp:^7} | {out:^7} |  {stats['status']}")
            
            lines.append("|")
        
        lines.append("+" + "-" * 81)
        lines.extend([
            "",
            "LEGENDA:",
            "  [L] Oswietlenie  [H] Ogrzewanie  [W] Nawadnianie  [?] Inne",
            "  OK - Wszystkie wejscia/wyjscia aktywne",
            "  !! - Niektore wejscia/wyjscia nieaktywne lub blad",
            "  Wejscia = aktywne czujniki/przyciski",
            "  Wyjscia = aktywne wyjscia cyfrowe (DO)"
        ])
        
        return "\n".join(lines)

    def report_full_io_table(self) -> str:
        lines = ["# PELNA LISTA PUNKTOW I/O", "", "| Sterownik | Lokalizacja | Typ | IO_ID | Status | Opis |", "|:----------|:------------|:----|:------|:-------|:-----|"]
        for device_id, device in sorted(self.devices.items()):
            for io in device.inputs:
                lines.append(f"| {device.address} | {device.location[:15]} | IN | {io.io_id} | {io.status} | {io.description[:40] if io.description else '-'} |")
            for io in device.outputs:
                lines.append(f"| {device.address} | {device.location[:15]} | OUT | {io.io_id} | {io.status} | {io.description[:40] if io.description else '-'} |")
        return "\n".join(lines)

    def report_by_function(self) -> str:
        categories = {'oswietlenie': [], 'ogrzewanie': [], 'nawadnianie': [], 'inne': []}
        for device_id, device in self.devices.items():
            desc_lower = device.description.lower()
            loc_lower = device.location.lower()
            if 'oświetleni' in desc_lower or any(x in loc_lower for x in ['wiatrołap', 'kuchnia', 'salon', 'korytarz', 'biuro', 'sypialnia', 'łazienka', 'wc', 'basia', 'poddasze', 'jadalnia', 'garderoba']):
                categories['oswietlenie'].append(device)
            elif 'ogrzewan' in desc_lower or 'temp' in desc_lower:
                categories['ogrzewanie'].append(device)
            elif 'nawadnian' in desc_lower or 'zraszacz' in desc_lower:
                categories['nawadnianie'].append(device)
            else:
                categories['inne'].append(device)
        lines = ["# URZADZENIA WEDLUG FUNKCJI", ""]
        for cat_name, devices in categories.items():
            if devices:
                lines.append(f"\n## {cat_name.upper()} ({len(devices)} urzadzen)")
                for dev in self._sort_devices_by_id(devices):
                    lines.append(f"  * {dev.address.split('.')[-1]:>3}: {dev.location}")
        return "\n".join(lines)

    def generate_mermaid_diagram(self) -> str:
        lines = ["```mermaid", "graph TD", "    subgraph RS485[Magistrala RS485]"]
        for device_id, device in self.devices.items():
            addr = device.address.replace('.', '_')
            lines.append(f'        DEV_{addr}["{device.address}<br/>{device.location}"]')
        lines.extend(["    end", "```"])
        return "\n".join(lines)

    def export_csv(self, output_path: str) -> None:
        with open(output_path, 'w', newline='', encoding='utf-8') as f:
            writer = csv.writer(f, delimiter=';')
            writer.writerow(['device_id', 'address', 'location', 'io_type', 'io_id', 'status', 'mapping', 'description'])
            for device_id, device in sorted(self.devices.items()):
                for io in device.inputs:
                    writer.writerow([device.device_id, device.address, device.location, 'INPUT', io.io_id, io.status, io.mapping, io.description])
                for io in device.outputs:
                    writer.writerow([device.device_id, device.address, device.location, 'OUTPUT', io.io_id, io.status, io.mapping, io.description])
        print(f"Eksportowano CSV: {output_path}")

    def export_json(self, output_path: str) -> None:
        data = {'project': {'id': self.data['project'].get('id'), 'title': self.data['project'].get('title'), 'generated_at': datetime.now().isoformat()}, 'devices': [dev.to_dict() for dev in self.devices.values()]}
        with open(output_path, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        print(f"Eksportowano JSON: {output_path}")

    def export_markdown(self, output_path: str) -> None:
        """Eksport pełnej dokumentacji do formatu Markdown"""
        md_lines = []
        proj = self.data.get('project', {})
        
        md_lines.append(f"# {proj.get('name', 'Projekt')}")
        md_lines.append("")
        if proj.get('description'):
            md_lines.append(f"**Opis:** {proj['description']}")
        if proj.get('version'):
            md_lines.append(f"**Wersja:** {proj['version']}")
        if proj.get('last_update'):
            md_lines.append(f"**Ostatnia aktualizacja:** {proj['last_update']}")
        md_lines.append("")
        
        hw_spec = proj.get('hardware_specification', {})
        if 'bus' in hw_spec:
            bus = hw_spec['bus']
            md_lines.append("## Specyfikacja magistrali")
            md_lines.append("")
            md_lines.append(f"- **Typ:** {bus.get('type', 'N/A')}")
            md_lines.append(f"- **ID:** {bus.get('bus_id', 'N/A')}")
            md_lines.append(f"- **Prędkość:** {bus.get('baud_rate', 'N/A')} bps")
            md_lines.append(f"- **Parametry:** {bus.get('data_bits', '?')}{bus.get('parity', '?')}{bus.get('stop_bits', '?')}")
            md_lines.append("")
        
        md_lines.append("## Statystyki")
        md_lines.append("")
        md_lines.append(f"- **Liczba urządzeń:** {len(self.devices)}")
        total_io = sum(len(d.inputs) + len(d.outputs) for d in self.devices.values())
        md_lines.append(f"- **Łączna liczba I/O:** {total_io}")
        md_lines.append("")
        
        md_lines.append("## Lista urządzeń")
        md_lines.append("")
        md_lines.append("| ID | Model | Adres | Lokalizacja | I/O |")
        md_lines.append("|:---|:----|:---------|:--------|----:|")
        for dev in sorted(self.devices.values(), key=lambda d: (d.location or "", d.device_id)):
            io_count = len(dev.inputs + dev.outputs)
            md_lines.append(f"| {dev.device_id} | {dev.model_id or 'N/A'} | {dev.address or 'N/A'} | {dev.location or 'N/A'} | {io_count} |")
        md_lines.append("")
        
        md_lines.append("## Szczegóły urządzeń")
        md_lines.append("")
        
        for dev in sorted(self.devices.values(), key=lambda d: (d.location or "", d.device_id)):
            md_lines.append(f"### {dev.device_id}")
            md_lines.append("")
            if dev.description:
                md_lines.append(f"**{dev.description}**")
                md_lines.append("")
            md_lines.append(f"- Model: {dev.model_id or 'N/A'}")
            md_lines.append(f"- Adres: {dev.address or 'N/A'}")
            md_lines.append(f"- Lokalizacja: {dev.location or 'N/A'}")
            if dev.location:
                md_lines.append(f"- Lokalizacja: {dev.location}")
            md_lines.append("")
            
            if dev.inputs + dev.outputs:
                md_lines.append("| I/O | Typ | Status | Opis |")
                md_lines.append("|:----|:----|:------:|:-----|")
                for io in dev.inputs + dev.outputs:
                    status_icon = {'ok': '✅', 'err': '❌', 'nc': '⚫', '??': '❓', '!': '⚠️'}.get(io.status, io.status)
                    desc = io.description.replace('|', '\\|') if io.description else ''
                    io_type = io.io_id.split(".")[0] if "." in io.io_id else "io"
                    md_lines.append(f"| {io.io_id} | {io_type} | {status_icon} | {desc} |")
                md_lines.append("")
        
        md_lines.append("## Legenda statusów")
        md_lines.append("")
        md_lines.append("- ✅ `ok` - Połączenie działa poprawnie")
        md_lines.append("- ❌ `err` - Błąd połączenia")
        md_lines.append("- ⚫ `nc` - Niepodłączone")
        md_lines.append("- ❓ `??` - Status nieznany")
        md_lines.append("- ⚠️ `!` - Do zrobienia/sprawdzenia")
        md_lines.append("")
        
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write('\n'.join(md_lines))
        print(f"Eksportowano Markdown: {output_path}")

    def update_io_description(self, device_id: str, io_id: str, new_description: str) -> bool:
        hw_spec = self.data.get('project', {}).get('hardware_specification', {})
        for dev in hw_spec.get('devices', []):
            if dev.get('device_id') == device_id:
                for io in dev.get('io', []):
                    if io.get('io_id') == io_id:
                        io['description'] = new_description
                        self._parse_devices()
                        print(f"Zaktualizowano: {device_id}/{io_id}")
                        return True
        print(f"Nie znaleziono: {device_id}/{io_id}")
        return False

    def update_io_status(self, device_id: str, io_id: str, new_status: str) -> bool:
        hw_spec = self.data.get('project', {}).get('hardware_specification', {})
        for dev in hw_spec.get('devices', []):
            if dev.get('device_id') == device_id:
                for io in dev.get('io', []):
                    if io.get('io_id') == io_id:
                        io['status'] = new_status
                        self._parse_devices()
                        print(f"Status zmieniony: {device_id}/{io_id} -> {new_status}")
                        return True
        print(f"Nie znaleziono: {device_id}/{io_id}")
        return False

    def save(self, output_path: str = None) -> None:
        path = output_path or self.file_path
        with open(path, 'w', encoding='utf-8') as f:
            yaml.dump(self.data, f, allow_unicode=True, default_flow_style=False, sort_keys=False)
        print(f"Zapisano: {path}")


def main():
    parser = argparse.ArgumentParser(description='IB Documentation Manager')
    parser.add_argument('file', help='Plik YAML z dokumentacją')
    parser.add_argument('--stats', action='store_true', help='Pokaż statystyki')
    parser.add_argument('--report', choices=['summary', 'connections'], help='Generuj raport')
    parser.add_argument('--search', help='Szukaj frazy w opisach')
    parser.add_argument('--export-csv', metavar='FILE', help='Eksport do CSV')
    parser.add_argument('--export-json', metavar='FILE', help='Eksport do JSON')
    parser.add_argument('--export-md', metavar='FILE', help='Eksport do Markdown')
    parser.add_argument('--interactive', '-i', action='store_true', help='Tryb interaktywny')
    args = parser.parse_args()

    manager = IBDocumentManager(args.file)

    if args.stats:
        manager.report_stats()
    elif args.report == 'summary':
        print(manager.report_summary_table())
    elif args.report == 'connections':
        print(manager.report_connections())
    elif args.search:
        manager.search_description(args.search)
    elif args.export_csv:
        manager.export_csv(args.export_csv)
    elif args.export_json:
        manager.export_json(args.export_json)
    elif args.export_md:
        manager.export_markdown(args.export_md)
    elif args.interactive:
        print("\nTryb interaktywny. Komendy: stats, summary, connections, search, export, edit, save, quit")
        while True:
            try:
                command = input("\n> ").strip().lower()
            except EOFError:
                break
            
            if command in ('quit', 'exit', 'q'):
                break
            elif command == 'stats':
                manager.report_stats()
            elif command == 'summary':
                print(manager.report_summary_table())
            elif command == 'connections':
                print(manager.report_connections())
            elif command == 'search':
                phrase = input("Fraza: ").strip()
                manager.search_description(phrase)
            elif command == 'export':
                print("Format: csv, json, md")
                fmt = input("Format: ").strip().lower()
                filename = input("Nazwa pliku: ").strip()
                if fmt == 'csv':
                    manager.export_csv(filename)
                elif fmt == 'json':
                    manager.export_json(filename)
                elif fmt == 'md':
                    manager.export_markdown(filename)
            elif command == 'edit':
                print("\nOpcje edycji: desc, status")
                edit_type = input("Typ edycji: ").strip().lower()
                if edit_type == 'desc':
                    device_id = input("Device ID (np. rs.0.id.30): ").strip()
                    io_id = input("IO ID (np. input.t.0): ").strip()
                    new_desc = input("Nowy opis: ").strip()
                    manager.update_io_description(device_id, io_id, new_desc)
                elif edit_type == 'status':
                    device_id = input("Device ID: ").strip()
                    io_id = input("IO ID: ").strip()
                    new_status = input("Nowy status (ok/err/nc/??/!): ").strip()
                    manager.update_io_status(device_id, io_id, new_status)
            elif command == 'save':
                filename = input("Nazwa pliku (Enter = nadpisz oryginalny): ").strip()
                manager.save(filename if filename else None)
            elif command == 'help':
                print("Komendy: stats, summary, connections, search, export, edit, save, quit")
            else:
                print("Nieznana komenda. Wpisz 'help' dla pomocy.")
    else:
        parser.print_help()

    return 0


if __name__ == '__main__':
    exit(main())
