"""
Views для управления отметками сигналов (привязаны к TechAnalyze).
"""

import json
from datetime import timedelta

from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.paginator import Paginator
from django.db import transaction
from django.db.models import Count, Max, Min, Prefetch, Q
from django.http import JsonResponse
from django.shortcuts import render, get_object_or_404
from django.utils import timezone
from django.views import View

from mainapp.models import (
    TechAnalyze,
    ObjectMark,
    CustomUser,
    Satellite,
    Polarization,
    Modulation,
    Standard,
)
from mainapp.permissions import PermissionRequiredMixin, has_permission


class SignalMarksView(LoginRequiredMixin, View):
    """
    Главное представление для работы с отметками сигналов.
    Содержит две вкладки: история отметок и проставление новых.
    """
    
    def get(self, request):
        satellites = Satellite.objects.filter(
            tech_analyzes__isnull=False
        ).distinct().order_by('name')
        
        satellite_id = request.GET.get('satellite_id')
        selected_satellite = None
        
        if satellite_id:
            try:
                selected_satellite = Satellite.objects.get(id=satellite_id)
            except Satellite.DoesNotExist:
                pass
        
        # Справочники для модального окна создания теханализа
        polarizations = Polarization.objects.all().order_by('name')
        modulations = Modulation.objects.all().order_by('name')
        standards = Standard.objects.all().order_by('name')
        
        context = {
            'satellites': satellites,
            'selected_satellite': selected_satellite,
            'selected_satellite_id': int(satellite_id) if satellite_id and satellite_id.isdigit() else None,
            'full_width_page': True,
            'polarizations': polarizations,
            'modulations': modulations,
            'standards': standards,
        }
        
        return render(request, 'mainapp/signal_marks.html', context)


class SignalMarksHistoryAPIView(LoginRequiredMixin, View):
    """
    API для получения истории отметок с фиксированными 15 колонками.
    Делит выбранный временной диапазон на 15 равных периодов.
    """
    
    NUM_COLUMNS = 15  # Фиксированное количество колонок
    
    def get(self, request):
        from datetime import datetime
        from django.utils.dateparse import parse_date
        
        satellite_id = request.GET.get('satellite_id')
        date_from = request.GET.get('date_from')
        date_to = request.GET.get('date_to')
        page = int(request.GET.get('page', 1))
        size = int(request.GET.get('size', 50))
        
        if not satellite_id:
            return JsonResponse({'error': 'Не выбран спутник'}, status=400)
        
        # Базовый queryset теханализов для спутника
        tech_analyzes = TechAnalyze.objects.filter(
            satellite_id=satellite_id
        ).select_related(
            'polarization', 'modulation', 'standard'
        ).order_by('frequency', 'name')
        
        # Базовый фильтр отметок по спутнику
        marks_base_qs = ObjectMark.objects.filter(
            tech_analyze__satellite_id=satellite_id
        ).select_related('created_by__user', 'tech_analyze')
        
        # Определяем диапазон дат
        parsed_date_from = None
        parsed_date_to = None
        
        if date_from:
            parsed_date_from = parse_date(date_from)
            if parsed_date_from:
                marks_base_qs = marks_base_qs.filter(timestamp__date__gte=parsed_date_from)
        
        if date_to:
            parsed_date_to = parse_date(date_to)
            if parsed_date_to:
                marks_base_qs = marks_base_qs.filter(timestamp__date__lte=parsed_date_to)
        
        # Если даты не указаны, берём из данных
        date_range = marks_base_qs.aggregate(
            min_date=Min('timestamp'),
            max_date=Max('timestamp')
        )
        
        min_date = date_range['min_date']
        max_date = date_range['max_date']
        
        if not min_date or not max_date:
            return JsonResponse({
                'periods': [],
                'data': [],
                'last_page': 1,
                'total': 0,
                'message': 'Нет отметок в выбранном диапазоне',
            })
        
        # Используем указанные даты или данные из БД
        start_dt = datetime.combine(parsed_date_from, datetime.min.time()) if parsed_date_from else min_date
        end_dt = datetime.combine(parsed_date_to, datetime.max.time()) if parsed_date_to else max_date
        
        # Делаем timezone-aware если нужно
        if timezone.is_naive(start_dt):
            start_dt = timezone.make_aware(start_dt)
        if timezone.is_naive(end_dt):
            end_dt = timezone.make_aware(end_dt)
        
        # Вычисляем длительность периода
        total_duration = end_dt - start_dt
        period_duration = total_duration / self.NUM_COLUMNS
        
        # Генерируем границы периодов
        periods = []
        for i in range(self.NUM_COLUMNS):
            period_start = start_dt + (period_duration * i)
            period_end = start_dt + (period_duration * (i + 1))
            periods.append({
                'start': period_start,
                'end': period_end,
                'label': self._format_period_label(period_start, period_end, total_duration),
            })
        
        # Пагинация теханализов (size=0 означает "все записи")
        if size == 0:
            # Все записи без пагинации
            page_obj = tech_analyzes
            num_pages = 1
            total_count = tech_analyzes.count()
        else:
            paginator = Paginator(tech_analyzes, size)
            page_obj = paginator.get_page(page)
            num_pages = paginator.num_pages
            total_count = paginator.count
        
        # Формируем данные
        data = []
        for ta in page_obj:
            row = {
                'id': ta.id,
                'name': ta.name,
                'marks': [],
            }
            
            # Получаем все отметки для этого теханализа
            ta_marks = list(marks_base_qs.filter(tech_analyze=ta).order_by('-timestamp'))
            
            # Для каждого периода находим последнюю отметку
            for period in periods:
                mark_in_period = None
                for mark in ta_marks:
                    if period['start'] <= mark.timestamp < period['end']:
                        mark_in_period = mark
                        break  # Берём первую (последнюю по времени, т.к. сортировка -timestamp)
                
                if mark_in_period:
                    # Конвертируем в локальное время (Europe/Moscow)
                    local_time = timezone.localtime(mark_in_period.timestamp)
                    row['marks'].append({
                        'mark': mark_in_period.mark,
                        'user': str(mark_in_period.created_by) if mark_in_period.created_by else '-',
                        'time': local_time.strftime('%d.%m %H:%M'),
                    })
                else:
                    row['marks'].append(None)
            
            data.append(row)
        
        return JsonResponse({
            'periods': [p['label'] for p in periods],
            'data': data,
            'last_page': num_pages,
            'total': total_count,
        })
    
    def _format_period_label(self, start, end, total_duration):
        """Форматирует метку периода (диапазон) в зависимости от общей длительности."""
        # Конвертируем в локальное время
        local_start = timezone.localtime(start)
        local_end = timezone.localtime(end)
        total_days = total_duration.days
        
        if total_days <= 1:
            # Показываем часы: "10:00<br>12:00"
            return f"{local_start.strftime('%H:%M')}<br>{local_end.strftime('%H:%M')}"
        elif total_days <= 7:
            # Показываем день и время с переносом
            if local_start.date() == local_end.date():
                # Один день: "01.12<br>10:00-14:00"
                return f"{local_start.strftime('%d.%m')}<br>{local_start.strftime('%H:%M')}-{local_end.strftime('%H:%M')}"
            else:
                # Разные дни: "01.12 10:00<br>02.12 10:00"
                return f"{local_start.strftime('%d.%m %H:%M')}<br>{local_end.strftime('%d.%m %H:%M')}"
        elif total_days <= 60:
            # Показываем дату: "01.12-05.12"
            return f"{local_start.strftime('%d.%m')}-{local_end.strftime('%d.%m')}"
        else:
            # Показываем месяц: "01.12.24-15.12.24"
            return f"{local_start.strftime('%d.%m.%y')}-{local_end.strftime('%d.%m.%y')}"


class SignalMarksEntryAPIView(LoginRequiredMixin, View):
    """
    API для получения данных теханализов для проставления отметок.
    """
    
    def get(self, request):
        satellite_id = request.GET.get('satellite_id')
        page = int(request.GET.get('page', 1))
        size_param = request.GET.get('size', '100')
        search = request.GET.get('search', '').strip()
        
        # Обработка size: "true" означает "все записи", иначе число
        if size_param == 'true' or size_param == '0':
            size = 0  # Все записи
        else:
            try:
                size = int(size_param)
            except (ValueError, TypeError):
                size = 100
        
        if not satellite_id:
            return JsonResponse({'error': 'Не выбран спутник'}, status=400)
        
        # Базовый queryset
        tech_analyzes = TechAnalyze.objects.filter(
            satellite_id=satellite_id
        ).select_related(
            'polarization', 'modulation', 'standard'
        ).prefetch_related(
            Prefetch(
                'marks',
                queryset=ObjectMark.objects.select_related('created_by__user').order_by('-timestamp')[:1],
                to_attr='last_marks'
            )
        ).annotate(
            mark_count=Count('marks'),
            last_mark_date=Max('marks__timestamp'),
        ).order_by('frequency', 'name')
        
        # Поиск
        if search:
            tech_analyzes = tech_analyzes.filter(
                Q(name__icontains=search) |
                Q(id__icontains=search)
            )
        
        # Пагинация (size=0 означает "все записи")
        if size == 0:
            page_obj = tech_analyzes
            num_pages = 1
            total_count = tech_analyzes.count()
        else:
            paginator = Paginator(tech_analyzes, size)
            page_obj = paginator.get_page(page)
            num_pages = paginator.num_pages
            total_count = paginator.count
        
        # Формируем данные
        data = []
        for ta in page_obj:
            last_mark = ta.last_marks[0] if ta.last_marks else None
            
            # Проверяем, можно ли добавить новую отметку (прошло 5 минут)
            can_add_mark = True
            if last_mark and last_mark.timestamp:
                time_diff = timezone.now() - last_mark.timestamp
                can_add_mark = time_diff >= timedelta(minutes=5)
            
            data.append({
                'id': ta.id,
                'name': ta.name,
                'frequency': float(ta.frequency) if ta.frequency else 0,
                'freq_range': float(ta.freq_range) if ta.freq_range else 0,
                'polarization': ta.polarization.name if ta.polarization else '-',
                'bod_velocity': float(ta.bod_velocity) if ta.bod_velocity else 0,
                'modulation': ta.modulation.name if ta.modulation else '-',
                'standard': ta.standard.name if ta.standard else '-',
                'mark_count': ta.mark_count,
                'last_mark': {
                    'mark': last_mark.mark,
                    'timestamp': last_mark.timestamp.strftime('%d.%m.%Y %H:%M'),
                    'user': str(last_mark.created_by) if last_mark.created_by else '-',
                } if last_mark else None,
                'can_add_mark': can_add_mark,
            })
        
        return JsonResponse({
            'data': data,
            'last_page': num_pages,
            'total': total_count,
        })


class SaveSignalMarksView(LoginRequiredMixin, PermissionRequiredMixin, View):
    """
    API для сохранения отметок сигналов.
    Принимает массив отметок и сохраняет их в базу.
    """
    permission_required = 'mark_create'
    
    def post(self, request):
        try:
            data = json.loads(request.body)
            marks = data.get('marks', [])
            
            if not marks:
                return JsonResponse({
                    'success': False,
                    'error': 'Нет данных для сохранения'
                }, status=400)
            
            # Получаем CustomUser
            custom_user = None
            if hasattr(request.user, 'customuser'):
                custom_user = request.user.customuser
            else:
                custom_user, _ = CustomUser.objects.get_or_create(user=request.user)
            
            created_count = 0
            skipped_count = 0
            errors = []
            
            with transaction.atomic():
                for item in marks:
                    tech_analyze_id = item.get('tech_analyze_id')
                    mark_value = item.get('mark')
                    
                    if tech_analyze_id is None or mark_value is None:
                        continue
                    
                    try:
                        tech_analyze = TechAnalyze.objects.get(id=tech_analyze_id)
                        
                        # Проверяем, можно ли добавить отметку
                        last_mark = tech_analyze.marks.order_by('-timestamp').first()
                        if last_mark and last_mark.timestamp:
                            time_diff = timezone.now() - last_mark.timestamp
                            if time_diff < timedelta(minutes=5):
                                skipped_count += 1
                                continue
                        
                        # Создаём отметку с текущим временем
                        ObjectMark.objects.create(
                            tech_analyze=tech_analyze,
                            mark=mark_value,
                            timestamp=timezone.now(),
                            created_by=custom_user,
                        )
                        created_count += 1
                        
                    except TechAnalyze.DoesNotExist:
                        errors.append(f'Теханализ {tech_analyze_id} не найден')
                    except Exception as e:
                        errors.append(f'Ошибка для {tech_analyze_id}: {str(e)}')
            
            return JsonResponse({
                'success': True,
                'created': created_count,
                'skipped': skipped_count,
                'errors': errors if errors else None,
            })
            
        except json.JSONDecodeError:
            return JsonResponse({
                'success': False,
                'error': 'Неверный формат данных'
            }, status=400)
        except Exception as e:
            return JsonResponse({
                'success': False,
                'error': str(e)
            }, status=500)


class CreateTechAnalyzeView(LoginRequiredMixin, PermissionRequiredMixin, View):
    """
    API для создания нового теханализа из модального окна.
    """
    permission_required = 'tech_analyze_create'
    
    def post(self, request):
        try:
            data = json.loads(request.body)
            
            satellite_id = data.get('satellite_id')
            name = data.get('name', '').strip()
            
            if not satellite_id:
                return JsonResponse({
                    'success': False,
                    'error': 'Не указан спутник'
                }, status=400)
            
            if not name:
                return JsonResponse({
                    'success': False,
                    'error': 'Не указано имя'
                }, status=400)
            
            # Проверяем уникальность имени
            if TechAnalyze.objects.filter(name=name).exists():
                return JsonResponse({
                    'success': False,
                    'error': f'Теханализ с именем "{name}" уже существует'
                }, status=400)
            
            try:
                satellite = Satellite.objects.get(id=satellite_id)
            except Satellite.DoesNotExist:
                return JsonResponse({
                    'success': False,
                    'error': 'Спутник не найден'
                }, status=404)
            
            # Получаем или создаём справочные данные
            polarization_name = data.get('polarization', '').strip() or '-'
            polarization, _ = Polarization.objects.get_or_create(name=polarization_name)
            
            modulation_name = data.get('modulation', '').strip() or '-'
            modulation, _ = Modulation.objects.get_or_create(name=modulation_name)
            
            standard_name = data.get('standard', '').strip() or '-'
            standard, _ = Standard.objects.get_or_create(name=standard_name)
            
            # Обработка числовых полей
            def parse_float(val):
                if val:
                    try:
                        return float(str(val).replace(',', '.'))
                    except (ValueError, TypeError):
                        pass
                return 0
            
            # Получаем CustomUser
            custom_user = None
            if hasattr(request.user, 'customuser'):
                custom_user = request.user.customuser
            
            # Создаём теханализ
            tech_analyze = TechAnalyze.objects.create(
                name=name,
                satellite=satellite,
                frequency=parse_float(data.get('frequency')),
                freq_range=parse_float(data.get('freq_range')),
                bod_velocity=parse_float(data.get('bod_velocity')),
                polarization=polarization,
                modulation=modulation,
                standard=standard,
                note=data.get('note', '').strip(),
                created_by=custom_user,
            )
            
            return JsonResponse({
                'success': True,
                'tech_analyze': {
                    'id': tech_analyze.id,
                    'name': tech_analyze.name,
                    'frequency': float(tech_analyze.frequency) if tech_analyze.frequency else 0,
                    'freq_range': float(tech_analyze.freq_range) if tech_analyze.freq_range else 0,
                    'polarization': polarization.name,
                    'bod_velocity': float(tech_analyze.bod_velocity) if tech_analyze.bod_velocity else 0,
                    'modulation': modulation.name,
                    'standard': standard.name,
                }
            })
            
        except json.JSONDecodeError:
            return JsonResponse({
                'success': False,
                'error': 'Неверный формат данных'
            }, status=400)
        except Exception as e:
            return JsonResponse({
                'success': False,
                'error': str(e)
            }, status=500)


# Оставляем старые views для обратной совместимости (редирект на новую страницу)
class ObjectMarksListView(LoginRequiredMixin, View):
    """Редирект на новую страницу отметок."""
    
    def get(self, request):
        from django.shortcuts import redirect
        return redirect('mainapp:signal_marks')


class AddObjectMarkView(LoginRequiredMixin, View):
    """Устаревший endpoint - теперь используется SaveSignalMarksView."""
    
    def post(self, request):
        return JsonResponse({
            'success': False,
            'error': 'Этот endpoint устарел. Используйте /api/save-signal-marks/'
        }, status=410)


class UpdateObjectMarkView(LoginRequiredMixin, View):
    """Устаревший endpoint."""
    
    def post(self, request):
        return JsonResponse({
            'success': False,
            'error': 'Этот endpoint устарел.'
        }, status=410)
