"""
KRS API Routes - Admin blueprint

Migrated from app.py as part of the blueprint refactoring.
Contains API routes for KRS (National Court Register) audits.
"""

import logging
from datetime import datetime, date
from pathlib import Path

from flask import jsonify, request, send_file, current_app
from flask_login import current_user, login_required

from database import (
    SessionLocal,
    Company,
    Person,
    CompanyPerson,
    CompanyPKD,
    CompanyFinancialReport,
    KRSAudit,
    SystemRole
)
from utils.decorators import role_required
from . import bp

logger = logging.getLogger(__name__)


# Import limiter from app - will be initialized when app starts
def get_limiter():
    """Get rate limiter from current app."""
    return current_app.extensions.get('limiter')


def is_krs_audit_available():
    """Check if KRS audit service is available."""
    try:
        from krs_audit_service import parse_krs_pdf
        return True
    except ImportError:
        return False


def parse_date_str(date_val):
    """Helper to parse date from string or return date object as-is"""
    if date_val is None:
        return None
    if isinstance(date_val, date):
        return date_val
    if isinstance(date_val, str):
        try:
            return datetime.strptime(date_val, '%Y-%m-%d').date()
        except Exception:
            return None
    return None


def _import_krs_person(db, company_id, person_data, role_category, source_document):
    """Helper to import a person from KRS data"""
    pesel = person_data.get('pesel')
    nazwisko = person_data.get('nazwisko', '')
    imiona = person_data.get('imiona', '')
    rola = person_data.get('rola', '')

    # Find or create Person
    person = None
    if pesel:
        person = db.query(Person).filter_by(pesel=pesel).first()

    if not person:
        # Try to find by name
        person = db.query(Person).filter_by(
            nazwisko=nazwisko,
            imiona=imiona
        ).first()

    if not person:
        person = Person(
            pesel=pesel,
            nazwisko=nazwisko,
            imiona=imiona
        )
        db.add(person)
        db.flush()

    # Check if relation already exists
    existing_rel = db.query(CompanyPerson).filter_by(
        company_id=company_id,
        person_id=person.id,
        role_category=role_category
    ).first()

    if not existing_rel:
        cp = CompanyPerson(
            company_id=company_id,
            person_id=person.id,
            role=rola,
            role_category=role_category,
            source='ekrs.ms.gov.pl',
            source_document=source_document,
            fetched_at=datetime.now()
        )
        # Add shares info for shareholders
        if role_category == 'wspolnik':
            cp.shares_count = person_data.get('udzialy_liczba')
            if person_data.get('udzialy_wartosc'):
                cp.shares_value = person_data['udzialy_wartosc']
            if person_data.get('udzialy_procent'):
                cp.shares_percent = person_data['udzialy_procent']
        db.add(cp)


# ============================================================
# KRS AUDIT API ROUTES
# ============================================================

@bp.route('/krs-api/audit', methods=['POST'])
@login_required
@role_required(SystemRole.OFFICE_MANAGER)
def api_krs_audit_trigger():
    """
    API: Trigger KRS audit for a company (admin-only).

    Parses KRS PDF file and extracts all available data:
    - Basic info (KRS, NIP, REGON, name, legal form)
    - Capital and shares
    - Management board, shareholders, procurators
    - PKD codes
    - Financial reports

    Request JSON body:
    - company_id: Company ID (integer)

    Returns:
    - Success: Audit results saved to database
    - Error: Error message with status code
    """
    if not is_krs_audit_available():
        return jsonify({
            'success': False,
            'error': 'Usługa audytu KRS jest niedostępna.'
        }), 503

    from krs_audit_service import parse_krs_pdf

    data = request.get_json()
    if not data or not data.get('company_id'):
        return jsonify({
            'success': False,
            'error': 'Podaj company_id firmy do audytu.'
        }), 400

    company_id = data['company_id']

    db = SessionLocal()
    try:
        company = db.query(Company).filter_by(id=company_id, status='active').first()
        if not company:
            return jsonify({
                'success': False,
                'error': 'Firma nie znaleziona.'
            }), 404

        if not company.krs:
            return jsonify({
                'success': False,
                'error': f'Firma "{company.name}" nie ma numeru KRS.'
            }), 400

        # Find PDF file
        pdf_dir = Path('data/krs_pdfs')
        pdf_files = list(pdf_dir.glob(f'*{company.krs}*.pdf'))

        if not pdf_files:
            return jsonify({
                'success': False,
                'error': f'Nie znaleziono pliku PDF dla KRS {company.krs}. '
                        f'Pobierz odpis z ekrs.ms.gov.pl i umieść w data/krs_pdfs/'
            }), 404

        pdf_path = pdf_files[0]

        # Create audit record
        audit = KRSAudit(
            company_id=company.id,
            status='parsing',
            progress_percent=10,
            progress_message='Parsowanie pliku PDF...',
            pdf_filename=pdf_path.name,
            pdf_path=str(pdf_path)
        )
        db.add(audit)
        db.commit()

        # Parse PDF
        try:
            parsed_data = parse_krs_pdf(str(pdf_path), verbose=True)

            # Update audit with parsed data
            audit.status = 'completed'
            audit.progress_percent = 100
            audit.progress_message = 'Audyt zakończony pomyślnie'
            audit.extracted_krs = parsed_data.get('krs')
            audit.extracted_nazwa = parsed_data.get('nazwa')
            audit.extracted_nip = parsed_data.get('nip')
            audit.extracted_regon = parsed_data.get('regon')
            audit.extracted_forma_prawna = parsed_data.get('forma_prawna')
            audit.extracted_data_rejestracji = parse_date_str(parsed_data.get('data_rejestracji'))
            audit.extracted_kapital_zakladowy = parsed_data.get('kapital_zakladowy')
            audit.extracted_liczba_udzialow = parsed_data.get('liczba_udzialow')
            audit.extracted_sposob_reprezentacji = parsed_data.get('sposob_reprezentacji')
            audit.zarzad_count = len(parsed_data.get('zarzad', []))
            audit.wspolnicy_count = len(parsed_data.get('wspolnicy', []))
            audit.prokurenci_count = len(parsed_data.get('prokurenci', []))
            audit.pkd_count = 1 if parsed_data.get('pkd_przewazajacy') else 0
            audit.pkd_count += len(parsed_data.get('pkd_pozostale', []))

            # Convert non-JSON-serializable values for JSONB storage
            def make_json_serializable(obj):
                from decimal import Decimal
                if isinstance(obj, Decimal):
                    return float(obj)
                elif isinstance(obj, (datetime, date)):
                    return obj.isoformat()
                elif isinstance(obj, dict):
                    return {k: make_json_serializable(v) for k, v in obj.items()}
                elif isinstance(obj, list):
                    return [make_json_serializable(i) for i in obj]
                return obj

            audit.parsed_data = make_json_serializable(parsed_data)
            audit.pdf_downloaded_at = datetime.now()

            # Update company with parsed data
            if parsed_data.get('kapital_zakladowy'):
                company.capital_amount = parsed_data['kapital_zakladowy']
            if parsed_data.get('liczba_udzialow'):
                company.capital_shares_count = parsed_data['liczba_udzialow']
            if parsed_data.get('wartosc_nominalna_udzialu'):
                company.capital_share_value = parsed_data['wartosc_nominalna_udzialu']
            if parsed_data.get('data_rejestracji'):
                company.krs_registration_date = parse_date_str(parsed_data['data_rejestracji'])
            if parsed_data.get('sposob_reprezentacji'):
                company.krs_representation_rules = parsed_data['sposob_reprezentacji']
            if parsed_data.get('czas_trwania'):
                company.krs_duration = parsed_data['czas_trwania']
            company.krs_last_audit_at = datetime.now()
            company.krs_pdf_path = str(pdf_path)

            # Import PKD codes
            pkd_main = parsed_data.get('pkd_przewazajacy')
            if pkd_main:
                existing = db.query(CompanyPKD).filter_by(
                    company_id=company.id,
                    pkd_code=pkd_main['kod']
                ).first()
                if not existing:
                    db.add(CompanyPKD(
                        company_id=company.id,
                        pkd_code=pkd_main['kod'],
                        pkd_description=pkd_main['opis'],
                        is_primary=True,
                        source='ekrs'
                    ))
                # Also update Company.pkd_code
                company.pkd_code = pkd_main['kod']
                company.pkd_description = pkd_main['opis']

            for pkd in parsed_data.get('pkd_pozostale', []):
                existing = db.query(CompanyPKD).filter_by(
                    company_id=company.id,
                    pkd_code=pkd['kod']
                ).first()
                if not existing:
                    db.add(CompanyPKD(
                        company_id=company.id,
                        pkd_code=pkd['kod'],
                        pkd_description=pkd['opis'],
                        is_primary=False,
                        source='ekrs'
                    ))

            # Import people (zarząd, wspólnicy)
            for person_data in parsed_data.get('zarzad', []):
                _import_krs_person(db, company.id, person_data, 'zarzad', pdf_path.name)

            for person_data in parsed_data.get('wspolnicy', []):
                _import_krs_person(db, company.id, person_data, 'wspolnik', pdf_path.name)

            for person_data in parsed_data.get('prokurenci', []):
                _import_krs_person(db, company.id, person_data, 'prokurent', pdf_path.name)

            # Import financial reports
            for report in parsed_data.get('sprawozdania_finansowe', []):
                existing = db.query(CompanyFinancialReport).filter_by(
                    company_id=company.id,
                    period_start=parse_date_str(report.get('okres_od')),
                    period_end=parse_date_str(report.get('okres_do'))
                ).first()
                if not existing:
                    db.add(CompanyFinancialReport(
                        company_id=company.id,
                        period_start=parse_date_str(report.get('okres_od')),
                        period_end=parse_date_str(report.get('okres_do')),
                        filed_at=parse_date_str(report.get('data_zlozenia')),
                        source='ekrs'
                    ))

            db.commit()

            logger.info(f"KRS audit completed for {company.name} (KRS: {company.krs})")

            return jsonify({
                'success': True,
                'message': f'Audyt KRS zakończony dla {company.name}',
                'company_id': company.id,
                'data': {
                    'krs': parsed_data.get('krs'),
                    'nazwa': parsed_data.get('nazwa'),
                    'nip': parsed_data.get('nip'),
                    'regon': parsed_data.get('regon'),
                    'kapital': float(parsed_data.get('kapital_zakladowy', 0) or 0),
                    'liczba_udzialow': parsed_data.get('liczba_udzialow'),
                    'zarzad_count': len(parsed_data.get('zarzad', [])),
                    'wspolnicy_count': len(parsed_data.get('wspolnicy', [])),
                    'prokurenci_count': len(parsed_data.get('prokurenci', [])),
                    'pkd_count': audit.pkd_count
                }
            })

        except Exception as e:
            audit.status = 'error'
            audit.progress_percent = 0
            audit.error_message = str(e)
            db.commit()
            logger.error(f"KRS audit failed for {company.name}: {e}")
            return jsonify({
                'success': False,
                'error': f'Błąd parsowania PDF: {str(e)}'
            }), 500

    finally:
        db.close()


@bp.route('/krs-api/audit/batch', methods=['POST'])
@login_required
@role_required(SystemRole.OFFICE_MANAGER)
def api_krs_audit_batch():
    """
    API: Trigger batch KRS audit for all companies with KRS numbers.

    This runs audits sequentially to avoid overloading the system.
    Returns progress updates via the response.
    """
    if not is_krs_audit_available():
        return jsonify({
            'success': False,
            'error': 'Usługa audytu KRS jest niedostępna.'
        }), 503

    from krs_audit_service import parse_krs_pdf

    db = SessionLocal()
    try:
        # Get companies with KRS that haven't been audited recently
        companies = db.query(Company).filter(
            Company.status == 'active',
            Company.krs.isnot(None),
            Company.krs != ''
        ).order_by(Company.name).all()

        results = {
            'total': len(companies),
            'success': 0,
            'failed': 0,
            'skipped': 0,
            'details': []
        }

        pdf_dir = Path('data/krs_pdfs')

        for company in companies:
            # Find PDF file
            pdf_files = list(pdf_dir.glob(f'*{company.krs}*.pdf'))

            if not pdf_files:
                results['skipped'] += 1
                results['details'].append({
                    'company': company.name,
                    'krs': company.krs,
                    'status': 'skipped',
                    'reason': 'Brak pliku PDF'
                })
                continue

            pdf_path = pdf_files[0]

            try:
                parsed_data = parse_krs_pdf(str(pdf_path))

                # Update company
                if parsed_data.get('kapital_zakladowy'):
                    company.capital_amount = parsed_data['kapital_zakladowy']
                if parsed_data.get('liczba_udzialow'):
                    company.capital_shares_count = parsed_data['liczba_udzialow']
                company.krs_last_audit_at = datetime.now()
                company.krs_pdf_path = str(pdf_path)

                results['success'] += 1
                results['details'].append({
                    'company': company.name,
                    'krs': company.krs,
                    'status': 'success'
                })

            except Exception as e:
                results['failed'] += 1
                results['details'].append({
                    'company': company.name,
                    'krs': company.krs,
                    'status': 'error',
                    'reason': str(e)
                })

        db.commit()

        return jsonify({
            'success': True,
            'message': f'Audyt zakończony: {results["success"]} sukces, '
                      f'{results["failed"]} błędów, {results["skipped"]} pominiętych',
            'results': results
        })

    finally:
        db.close()


@bp.route('/krs-api/pdf/<int:company_id>')
@login_required
@role_required(SystemRole.OFFICE_MANAGER)
def api_krs_pdf_download(company_id):
    """
    API: Download/serve KRS PDF file for a company.
    """
    db = SessionLocal()
    try:
        company = db.query(Company).filter_by(id=company_id).first()
        if not company:
            return jsonify({'error': 'Firma nie znaleziona'}), 404

        if not company.krs_pdf_path:
            return jsonify({'error': 'Brak pliku PDF'}), 404

        pdf_path = Path(company.krs_pdf_path)
        if not pdf_path.exists():
            return jsonify({'error': 'Plik PDF nie istnieje'}), 404

        return send_file(
            str(pdf_path),
            mimetype='application/pdf',
            as_attachment=False,
            download_name=pdf_path.name
        )
    finally:
        db.close()
