"""
Django management команда для создания бэкапа базы данных.
Использование: python manage.py backup_database
"""

import os
import gzip
from datetime import datetime
from django.core.management.base import BaseCommand
from django.conf import settings
from django.db import connection
import logging

logger = logging.getLogger(__name__)


class Command(BaseCommand):
    help = 'Создает резервную копию базы данных PostgreSQL'

    def add_arguments(self, parser):
        parser.add_argument(
            '--output-dir',
            type=str,
            default=os.path.join(settings.BASE_DIR.parent, 'backups', 'database'),
            help='Директория для сохранения бэкапа',
        )
        parser.add_argument(
            '--retention-days',
            type=int,
            default=30,
            help='Количество дней для хранения бэкапов (по умолчанию 30)',
        )
        parser.add_argument(
            '--compress',
            action='store_true',
            help='Сжимать бэкап с помощью gzip',
        )

    def handle(self, *args, **options):
        output_dir = options['output_dir']
        retention_days = options['retention_days']
        compress = options['compress']

        # Создаем директорию для бэкапов
        os.makedirs(output_dir, exist_ok=True)

        # Получаем параметры подключения к БД
        db_config = settings.DATABASES['default']
        db_name = db_config['NAME']
        db_user = db_config['USER']
        db_host = db_config.get('HOST', 'localhost')
        db_port = db_config.get('PORT', '5432')

        # Формируем имя файла бэкапа
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        backup_filename = f'db_backup_{timestamp}.sql'
        if compress:
            backup_filename += '.gz'
        
        backup_path = os.path.join(output_dir, backup_filename)

        self.stdout.write(f'Создание бэкапа базы данных {db_name}...')

        try:
            # Используем pg_dump через subprocess
            import subprocess
            
            # Формируем команду pg_dump
            pg_dump_cmd = [
                'pg_dump',
                '-h', db_host,
                '-p', str(db_port),
                '-U', db_user,
                '-d', db_name,
                '--no-password',  # Используем .pgpass или переменные окружения
                '--format=plain',
                '--verbose',
            ]

            # Устанавливаем пароль через переменную окружения
            env = os.environ.copy()
            if 'PASSWORD' in db_config:
                env['PGPASSWORD'] = db_config['PASSWORD']

            # Выполняем pg_dump
            process = subprocess.Popen(
                pg_dump_cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                env=env,
            )

            stdout, stderr = process.communicate()

            if process.returncode != 0:
                error_msg = stderr.decode('utf-8')
                self.stdout.write(
                    self.style.ERROR(f'Ошибка создания бэкапа: {error_msg}')
                )
                return

            # Сохраняем бэкап
            if compress:
                with gzip.open(backup_path, 'wb') as f:
                    f.write(stdout)
            else:
                with open(backup_path, 'wb') as f:
                    f.write(stdout)

            # Получаем размер файла
            file_size = os.path.getsize(backup_path)
            file_size_mb = file_size / (1024 * 1024)

            self.stdout.write(
                self.style.SUCCESS(
                    f'✓ Бэкап успешно создан: {backup_path} ({file_size_mb:.2f} MB)'
                )
            )

            # Удаляем старые бэкапы
            self.cleanup_old_backups(output_dir, retention_days)

        except FileNotFoundError:
            self.stdout.write(
                self.style.ERROR(
                    'pg_dump не найден. Убедитесь, что PostgreSQL клиент установлен.'
                )
            )
        except Exception as e:
            self.stdout.write(
                self.style.ERROR(f'Ошибка при создании бэкапа: {str(e)}')
            )
            logger.error(f'Backup error: {e}', exc_info=True)

    def cleanup_old_backups(self, backup_dir, retention_days):
        """Удаляет старые бэкапы."""
        from datetime import timedelta
        import time

        cutoff_time = time.time() - (retention_days * 24 * 60 * 60)

        deleted_count = 0
        for filename in os.listdir(backup_dir):
            if filename.startswith('db_backup_') and (filename.endswith('.sql') or filename.endswith('.sql.gz')):
                file_path = os.path.join(backup_dir, filename)
                if os.path.getmtime(file_path) < cutoff_time:
                    try:
                        os.remove(file_path)
                        deleted_count += 1
                        self.stdout.write(f'Удален старый бэкап: {filename}')
                    except Exception as e:
                        self.stdout.write(
                            self.style.WARNING(f'Не удалось удалить {filename}: {e}')
                        )

        if deleted_count > 0:
            self.stdout.write(
                self.style.SUCCESS(f'Удалено старых бэкапов: {deleted_count}')
            )

