"""
Push-Up Counter - Sistema de Logging
Módulo para registrar sessões, eventos, landmarks e métricas
"""

import json
import csv
import os
from datetime import datetime
from pathlib import Path
import time


class PushUpLogger:
    """Sistema de logging para o contador de flexões"""

    def __init__(self, logs_dir="pushup_logs"):
        """
        Inicializa o logger

        Args:
            logs_dir: Diretório onde os logs serão salvos
        """
        self.logs_dir = Path(logs_dir)
        self.logs_dir.mkdir(exist_ok=True)

        self.session_data = None
        self.session_id = None
        self.start_time = None
        self.frame_count = 0
        self.fps_samples = []
        self.last_frame_time = None

    def start_session(self):
        """Inicia uma nova sessão de treino"""
        self.session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
        self.start_time = datetime.now()
        self.frame_count = 0
        self.fps_samples = []
        self.last_frame_time = time.time()

        self.session_data = {
            "session_id": self.session_id,
            "start_time": self.start_time.isoformat(),
            "end_time": None,
            "duration_seconds": 0,
            "total_pushups": 0,
            "avg_fps": 0,
            "pushups": [],
            "frames": [],
            "metrics": {}
        }

        print(f"📊 [Logger] Sessão iniciada: {self.session_id}")

    def log_frame(self, landmarks, position, fps=None):
        """
        Registra dados de um frame

        Args:
            landmarks: Dicionário com coordenadas dos landmarks
            position: "up", "down" ou None
            fps: FPS atual (opcional, será calculado se não fornecido)
        """
        if self.session_data is None:
            return

        # Calcular FPS se não fornecido
        current_time = time.time()
        if fps is None and self.last_frame_time:
            frame_interval = current_time - self.last_frame_time
            if frame_interval > 0:
                fps = 1.0 / frame_interval

        self.last_frame_time = current_time

        if fps:
            self.fps_samples.append(fps)

        self.frame_count += 1

        # Registrar apenas a cada 5 frames para economizar espaço
        # (mas sempre registra frames de flexão)
        if self.frame_count % 5 == 0 or position in ["up", "down"]:
            frame_data = {
                "frame_number": self.frame_count,
                "timestamp": datetime.now().isoformat(),
                "fps": round(fps, 2) if fps else 0,
                "landmarks": landmarks,
                "position": position
            }

            self.session_data["frames"].append(frame_data)

    def log_pushup(self, count, landmarks_down=None, landmarks_up=None):
        """
        Registra uma flexão completa

        Args:
            count: Número da flexão
            landmarks_down: Coordenadas na posição baixa
            landmarks_up: Coordenadas na posição alta
        """
        if self.session_data is None:
            return

        timestamp = datetime.now()

        # Calcular duração desde a última flexão
        duration_from_previous = 0
        if len(self.session_data["pushups"]) > 0:
            last_timestamp = datetime.fromisoformat(
                self.session_data["pushups"][-1]["timestamp"]
            )
            duration_from_previous = (timestamp - last_timestamp).total_seconds()

        pushup_data = {
            "count": count,
            "timestamp": timestamp.isoformat(),
            "duration_from_previous": round(duration_from_previous, 2),
            "landmarks_down": landmarks_down,
            "landmarks_up": landmarks_up
        }

        self.session_data["pushups"].append(pushup_data)
        self.session_data["total_pushups"] = count

        print(f"📝 [Logger] Flexão #{count} registrada (Δt: {duration_from_previous:.2f}s)")

    def end_session(self):
        """Finaliza a sessão e salva os dados"""
        if self.session_data is None:
            print("⚠️ [Logger] Nenhuma sessão ativa para finalizar")
            return None

        end_time = datetime.now()
        duration = (end_time - self.start_time).total_seconds()

        # Atualizar dados finais
        self.session_data["end_time"] = end_time.isoformat()
        self.session_data["duration_seconds"] = round(duration, 2)
        self.session_data["avg_fps"] = round(
            sum(self.fps_samples) / len(self.fps_samples), 2
        ) if self.fps_samples else 0

        # Calcular métricas
        self.session_data["metrics"] = self._calculate_metrics()

        # Salvar em JSON
        json_path = self.logs_dir / f"session_{self.session_id}.json"
        with open(json_path, 'w', encoding='utf-8') as f:
            json.dump(self.session_data, f, indent=2, ensure_ascii=False)

        # Salvar em CSV
        csv_path = self.logs_dir / f"session_{self.session_id}.csv"
        self._save_csv(csv_path)

        # Atualizar resumo geral
        self._update_summary()

        print(f"\n✅ [Logger] Sessão finalizada e salva:")
        print(f"   📄 JSON: {json_path}")
        print(f"   📊 CSV: {csv_path}")
        print(f"   ⏱️  Duração: {duration:.1f}s")
        print(f"   💪 Total: {self.session_data['total_pushups']} flexões")
        print(f"   🎥 FPS médio: {self.session_data['avg_fps']}")

        session_id = self.session_id
        self.session_data = None
        self.session_id = None

        return session_id

    def _calculate_metrics(self):
        """Calcula métricas da sessão"""
        pushups = self.session_data["pushups"]

        if len(pushups) == 0:
            return {
                "avg_time_per_pushup": 0,
                "fastest_pushup": 0,
                "slowest_pushup": 0,
                "avg_fps": self.session_data["avg_fps"],
                "detection_quality": 0,
                "total_frames": self.frame_count
            }

        # Tempos entre flexões (ignorando a primeira)
        times = [p["duration_from_previous"] for p in pushups[1:]]

        metrics = {
            "avg_time_per_pushup": round(sum(times) / len(times), 2) if times else 0,
            "fastest_pushup": round(min(times), 2) if times else 0,
            "slowest_pushup": round(max(times), 2) if times else 0,
            "avg_fps": self.session_data["avg_fps"],
            "detection_quality": round(len(pushups) / max(self.frame_count, 1), 4),
            "total_frames": self.frame_count
        }

        return metrics

    def _save_csv(self, csv_path):
        """Salva dados em formato CSV"""
        with open(csv_path, 'w', newline='', encoding='utf-8') as f:
            writer = csv.writer(f)

            # Cabeçalho
            writer.writerow([
                "Flexão",
                "Timestamp",
                "Tempo desde anterior (s)",
                "Ombro Esq Y",
                "Ombro Dir Y",
                "Cotovelo Esq Y",
                "Cotovelo Dir Y"
            ])

            # Dados
            for pushup in self.session_data["pushups"]:
                landmarks_up = pushup.get("landmarks_up", {})

                writer.writerow([
                    pushup["count"],
                    pushup["timestamp"],
                    pushup["duration_from_previous"],
                    landmarks_up.get("shoulder_left_y", ""),
                    landmarks_up.get("shoulder_right_y", ""),
                    landmarks_up.get("elbow_left_y", ""),
                    landmarks_up.get("elbow_right_y", "")
                ])

    def _update_summary(self):
        """Atualiza o arquivo summary.json com todas as sessões"""
        summary_path = self.logs_dir / "summary.json"

        # Carregar resumo existente
        if summary_path.exists():
            with open(summary_path, 'r', encoding='utf-8') as f:
                summary = json.load(f)
        else:
            summary = {
                "total_sessions": 0,
                "total_pushups": 0,
                "total_duration_seconds": 0,
                "sessions": []
            }

        # Adicionar nova sessão
        session_summary = {
            "session_id": self.session_id,
            "date": self.start_time.strftime("%Y-%m-%d"),
            "time": self.start_time.strftime("%H:%M:%S"),
            "duration_seconds": self.session_data["duration_seconds"],
            "total_pushups": self.session_data["total_pushups"],
            "avg_fps": self.session_data["avg_fps"],
            "avg_time_per_pushup": self.session_data["metrics"]["avg_time_per_pushup"]
        }

        summary["sessions"].append(session_summary)
        summary["total_sessions"] = len(summary["sessions"])
        summary["total_pushups"] += self.session_data["total_pushups"]
        summary["total_duration_seconds"] += self.session_data["duration_seconds"]

        # Calcular recordes
        all_pushups = [s["total_pushups"] for s in summary["sessions"]]
        summary["record_pushups"] = max(all_pushups) if all_pushups else 0
        summary["avg_pushups_per_session"] = round(
            summary["total_pushups"] / summary["total_sessions"], 2
        ) if summary["total_sessions"] > 0 else 0

        # Salvar
        with open(summary_path, 'w', encoding='utf-8') as f:
            json.dump(summary, f, indent=2, ensure_ascii=False)

        print(f"   📋 Resumo atualizado: {summary_path}")


# Função auxiliar para criar landmarks dict
def create_landmarks_dict(landmarks_obj):
    """
    Converte objeto landmarks do MediaPipe para dicionário

    Args:
        landmarks_obj: Objeto landmarks do MediaPipe

    Returns:
        Dicionário com coordenadas dos pontos principais
    """
    if landmarks_obj is None:
        return {}

    return {
        "shoulder_left_y": round(landmarks_obj[11].y, 4),
        "shoulder_right_y": round(landmarks_obj[12].y, 4),
        "elbow_left_y": round(landmarks_obj[13].y, 4),
        "elbow_right_y": round(landmarks_obj[14].y, 4),
        "shoulder_left_x": round(landmarks_obj[11].x, 4),
        "shoulder_right_x": round(landmarks_obj[12].x, 4),
        "elbow_left_x": round(landmarks_obj[13].x, 4),
        "elbow_right_x": round(landmarks_obj[14].x, 4),
    }
