"""
Board Routes (Rada Izby)
========================

Routes for board meeting management and PDF generation.

Endpoints - Meetings:
- GET /rada/                                    - List all meetings + board members
- GET /rada/posiedzenia                         - Redirect to /rada/
- GET/POST /rada/posiedzenia/nowe               - Create new meeting (office_manager+)
- GET/POST /rada/posiedzenia/<id>/edytuj        - Edit meeting (office_manager+)
- GET /rada/posiedzenia/<id>                    - View meeting details
- POST /rada/posiedzenia/<id>/publikuj-program  - Publish agenda (office_manager+)
- POST /rada/posiedzenia/<id>/publikuj-protokol - Publish protocol (office_manager+)
- GET /rada/posiedzenia/<id>/pdf-program        - Download agenda PDF
- GET /rada/posiedzenia/<id>/pdf-protokol       - Download protocol PDF
"""

from datetime import datetime
from flask import (
    render_template, request, redirect, url_for, flash,
    current_app, Response
)
from flask_login import login_required, current_user
from sqlalchemy import desc

from . import bp
from database import SessionLocal, BoardMeeting, SystemRole, User
from utils.decorators import rada_member_required, office_manager_required
from utils.helpers import sanitize_html
from datetime import date, time

try:
    import weasyprint
    HAS_WEASYPRINT = True
except (ImportError, OSError):
    HAS_WEASYPRINT = False


# =============================================================================
# MEETING ROUTES
# =============================================================================

@bp.route('/')
@login_required
@rada_member_required
def index():
    """Display list of board meetings and board members (main page)"""
    db = SessionLocal()
    try:
        meetings = db.query(BoardMeeting).order_by(
            desc(BoardMeeting.year),
            desc(BoardMeeting.meeting_number)
        ).all()

        # Get board members
        board_members = db.query(User).filter(
            User.is_rada_member == True,
            User.is_active == True
        ).order_by(User.name).all()

        can_manage = current_user.has_role(SystemRole.OFFICE_MANAGER)

        return render_template(
            'board/meetings_list.html',
            meetings=meetings,
            board_members=board_members,
            can_manage=can_manage
        )
    finally:
        db.close()


@bp.route('/posiedzenia')
@login_required
@rada_member_required
def meetings_list():
    """Redirect to main board page for backwards compatibility"""
    return redirect(url_for('board.index'))


@bp.route('/posiedzenia/nowe', methods=['GET', 'POST'])
@login_required
@office_manager_required
def meeting_create():
    """Create new board meeting"""
    db = SessionLocal()
    try:
        if request.method == 'POST':
            return _handle_meeting_form(db, meeting=None)

        # GET - show form with defaults
        # Get next meeting number for current year
        current_year = datetime.now().year
        last_meeting = db.query(BoardMeeting).filter(
            BoardMeeting.year == current_year
        ).order_by(desc(BoardMeeting.meeting_number)).first()

        next_number = (last_meeting.meeting_number + 1) if last_meeting else 1

        # Get board members for attendance list
        board_members = db.query(User).filter(
            User.is_rada_member == True,
            User.is_active == True
        ).order_by(User.name).all()

        # Default chairperson (Prezes - Leszek Glaza)
        default_chairperson = db.query(User).filter(
            User.email == 'leszek@rotor.pl'
        ).first()

        # Default secretary (Magdalena Klóska)
        default_secretary = db.query(User).filter(
            User.email.like('%kloska%')
        ).first()

        # Get staff users for secretary dropdown (OFFICE_MANAGER and above)
        staff_users = db.query(User).filter(
            User.role.in_(['OFFICE_MANAGER', 'ADMIN']),
            User.is_active == True
        ).order_by(User.name).all()

        return render_template(
            'board/meeting_form.html',
            meeting=None,
            form_data={
                'meeting_number': next_number,
                'year': current_year,
                'location': 'Siedziba Izby',
                'start_time': '16:00',
                'chairperson_id': default_chairperson.id if default_chairperson else None,
                'secretary_id': default_secretary.id if default_secretary else None,
            },
            board_members=board_members,
            staff_users=staff_users,
            is_edit=False
        )
    finally:
        db.close()


@bp.route('/posiedzenia/<int:meeting_id>/edytuj', methods=['GET', 'POST'])
@login_required
@office_manager_required
def meeting_edit(meeting_id):
    """Edit existing board meeting"""
    db = SessionLocal()
    try:
        meeting = db.query(BoardMeeting).filter(
            BoardMeeting.id == meeting_id
        ).first()

        if not meeting:
            flash('Posiedzenie nie zostało znalezione.', 'error')
            return redirect(url_for('board.index'))

        if request.method == 'POST':
            return _handle_meeting_form(db, meeting=meeting)

        # GET - show form with existing data
        board_members = db.query(User).filter(
            User.is_rada_member == True,
            User.is_active == True
        ).order_by(User.name).all()

        # Get staff users for secretary dropdown (OFFICE_MANAGER and above)
        staff_users = db.query(User).filter(
            User.role.in_(['OFFICE_MANAGER', 'ADMIN']),
            User.is_active == True
        ).order_by(User.name).all()

        return render_template(
            'board/meeting_form.html',
            meeting=meeting,
            form_data={
                'meeting_number': meeting.meeting_number,
                'year': meeting.year,
                'meeting_date': meeting.meeting_date.isoformat() if meeting.meeting_date else '',
                'start_time': meeting.start_time.strftime('%H:%M') if meeting.start_time else '',
                'end_time': meeting.end_time.strftime('%H:%M') if meeting.end_time else '',
                'location': meeting.location,
                'chairperson_id': meeting.chairperson_id,
                'secretary_id': meeting.secretary_id,
                'guests': meeting.guests,
                'agenda_items': meeting.agenda_items or [],
                'attendance': meeting.attendance or {},
                'proceedings': meeting.proceedings or [],
            },
            board_members=board_members,
            staff_users=staff_users,
            is_edit=True
        )
    finally:
        db.close()


@bp.route('/posiedzenia/<int:meeting_id>')
@login_required
@rada_member_required
def meeting_view(meeting_id):
    """View board meeting details"""
    db = SessionLocal()
    try:
        meeting = db.query(BoardMeeting).filter(
            BoardMeeting.id == meeting_id
        ).first()

        if not meeting:
            flash('Posiedzenie nie zostało znalezione.', 'error')
            return redirect(url_for('board.index'))

        # Get board members for attendance display
        board_members = db.query(User).filter(
            User.is_rada_member == True,
            User.is_active == True
        ).order_by(User.name).all()

        can_manage = current_user.has_role(SystemRole.OFFICE_MANAGER)

        return render_template(
            'board/meeting_view.html',
            meeting=meeting,
            board_members=board_members,
            can_manage=can_manage
        )
    finally:
        db.close()


@bp.route('/posiedzenia/<int:meeting_id>/publikuj-program', methods=['POST'])
@login_required
@office_manager_required
def meeting_publish_agenda(meeting_id):
    """Publish meeting agenda"""
    db = SessionLocal()
    try:
        meeting = db.query(BoardMeeting).filter(
            BoardMeeting.id == meeting_id
        ).first()

        if not meeting:
            flash('Posiedzenie nie zostało znalezione.', 'error')
            return redirect(url_for('board.index'))

        if meeting.status != BoardMeeting.STATUS_DRAFT:
            flash('Program został już opublikowany.', 'warning')
            return redirect(url_for('board.meeting_view', meeting_id=meeting_id))

        # Validate required fields
        if not meeting.meeting_date:
            flash('Data posiedzenia jest wymagana przed publikacją.', 'error')
            return redirect(url_for('board.meeting_edit', meeting_id=meeting_id))

        if not meeting.agenda_items:
            flash('Program posiedzenia jest wymagany przed publikacją.', 'error')
            return redirect(url_for('board.meeting_edit', meeting_id=meeting_id))

        # Publish
        meeting.status = BoardMeeting.STATUS_AGENDA_PUBLISHED
        meeting.agenda_published_at = datetime.now()
        meeting.updated_by = current_user.id
        meeting.updated_at = datetime.now()
        db.commit()

        flash(f'Program posiedzenia {meeting.meeting_identifier} został opublikowany.', 'success')
        current_app.logger.info(
            f"Meeting agenda published: {meeting.meeting_identifier} by user {current_user.id}"
        )

        return redirect(url_for('board.meeting_view', meeting_id=meeting_id))
    except Exception as e:
        db.rollback()
        current_app.logger.error(f"Failed to publish meeting agenda: {e}")
        flash('Błąd podczas publikowania programu.', 'error')
        return redirect(url_for('board.meeting_view', meeting_id=meeting_id))
    finally:
        db.close()


@bp.route('/posiedzenia/<int:meeting_id>/publikuj-protokol', methods=['POST'])
@login_required
@office_manager_required
def meeting_publish_protocol(meeting_id):
    """Publish meeting protocol"""
    db = SessionLocal()
    try:
        meeting = db.query(BoardMeeting).filter(
            BoardMeeting.id == meeting_id
        ).first()

        if not meeting:
            flash('Posiedzenie nie zostało znalezione.', 'error')
            return redirect(url_for('board.index'))

        if meeting.status == BoardMeeting.STATUS_PROTOCOL_PUBLISHED:
            flash('Protokół został już opublikowany.', 'warning')
            return redirect(url_for('board.meeting_view', meeting_id=meeting_id))

        # Validate required fields for protocol
        if not meeting.attendance:
            flash('Lista obecności jest wymagana przed publikacją protokołu.', 'error')
            return redirect(url_for('board.meeting_edit', meeting_id=meeting_id))

        if not meeting.proceedings:
            flash('Przebieg posiedzenia jest wymagany przed publikacją protokołu.', 'error')
            return redirect(url_for('board.meeting_edit', meeting_id=meeting_id))

        # Publish
        meeting.status = BoardMeeting.STATUS_PROTOCOL_PUBLISHED
        meeting.protocol_published_at = datetime.now()
        meeting.updated_by = current_user.id
        meeting.updated_at = datetime.now()
        db.commit()

        flash(f'Protokół z posiedzenia {meeting.meeting_identifier} został opublikowany.', 'success')
        current_app.logger.info(
            f"Meeting protocol published: {meeting.meeting_identifier} by user {current_user.id}"
        )

        return redirect(url_for('board.meeting_view', meeting_id=meeting_id))
    except Exception as e:
        db.rollback()
        current_app.logger.error(f"Failed to publish meeting protocol: {e}")
        flash('Błąd podczas publikowania protokołu.', 'error')
        return redirect(url_for('board.meeting_view', meeting_id=meeting_id))
    finally:
        db.close()


# =============================================================================
# MEETING PDF GENERATION
# =============================================================================

def _generate_meeting_pdf(meeting_id, pdf_type):
    """Generate PDF for meeting agenda or protocol.
    pdf_type: 'agenda' or 'protocol'
    """
    if not HAS_WEASYPRINT:
        flash('Generowanie PDF nie jest dostępne (brak biblioteki weasyprint).', 'error')
        return redirect(url_for('board.meeting_view', meeting_id=meeting_id))

    db = SessionLocal()
    try:
        meeting = db.query(BoardMeeting).filter(
            BoardMeeting.id == meeting_id
        ).first()

        if not meeting:
            flash('Posiedzenie nie zostało znalezione.', 'error')
            return redirect(url_for('board.index'))

        # Get board members for protocol attendance
        board_members = db.query(User).filter(
            User.is_rada_member == True,
            User.is_active == True
        ).order_by(User.name).all()

        # Render HTML template
        html_content = render_template(
            'board/meeting_pdf.html',
            meeting=meeting,
            board_members=board_members,
            pdf_type=pdf_type
        )

        # Generate PDF
        pdf_bytes = weasyprint.HTML(string=html_content).write_pdf()

        # Build filename
        if pdf_type == 'agenda':
            filename = f"Program_Posiedzenia_{meeting.meeting_identifier.replace('/', '_')}.pdf"
        else:
            filename = f"Protokol_Posiedzenia_{meeting.meeting_identifier.replace('/', '_')}.pdf"

        current_app.logger.info(
            f"Meeting PDF generated: {pdf_type} for {meeting.meeting_identifier} by user {current_user.id}"
        )

        return Response(
            pdf_bytes,
            mimetype='application/pdf',
            headers={
                'Content-Disposition': f'attachment; filename="{filename}"'
            }
        )
    except Exception as e:
        current_app.logger.error(f"Failed to generate meeting PDF: {e}")
        flash('Błąd podczas generowania PDF.', 'error')
        return redirect(url_for('board.meeting_view', meeting_id=meeting_id))
    finally:
        db.close()


@bp.route('/posiedzenia/<int:meeting_id>/pdf-program')
@login_required
@rada_member_required
def meeting_pdf_agenda(meeting_id):
    """Download meeting agenda as PDF"""
    return _generate_meeting_pdf(meeting_id, 'agenda')


@bp.route('/posiedzenia/<int:meeting_id>/pdf-protokol')
@login_required
@rada_member_required
def meeting_pdf_protocol(meeting_id):
    """Download meeting protocol as PDF"""
    return _generate_meeting_pdf(meeting_id, 'protocol')


# =============================================================================
# MEETING FORM HANDLER
# =============================================================================

def _handle_meeting_form(db, meeting=None):
    """Handle meeting form submission (create or update)"""
    import json

    # Get form data
    meeting_number = request.form.get('meeting_number', type=int)
    year = request.form.get('year', type=int)
    meeting_date_str = request.form.get('meeting_date', '')
    start_time_str = request.form.get('start_time', '')
    end_time_str = request.form.get('end_time', '')
    location = request.form.get('location', '').strip()
    chairperson_id = request.form.get('chairperson_id', type=int)
    secretary_id = request.form.get('secretary_id', type=int)
    guests = request.form.get('guests', '').strip()

    # Parse agenda items from JSON
    agenda_items_json = request.form.get('agenda_items', '[]')
    try:
        agenda_items = json.loads(agenda_items_json)
    except json.JSONDecodeError:
        agenda_items = []

    # Parse attendance from form (status: present/absent/unknown)
    attendance = {}
    for key, value in request.form.items():
        if key.startswith('attendance_status_'):
            user_id = key.replace('attendance_status_', '')
            initials = request.form.get(f'initials_{user_id}', '')
            status = value  # present, absent, or unknown
            attendance[user_id] = {
                'status': status,
                'present': status == 'present',  # Keep for backward compatibility
                'initials': initials
            }

    # Parse proceedings from JSON
    proceedings_json = request.form.get('proceedings', '[]')
    try:
        proceedings = json.loads(proceedings_json)
    except json.JSONDecodeError:
        proceedings = []

    # Sanitize text fields in proceedings to prevent stored XSS
    for proc in proceedings:
        if isinstance(proc, dict):
            for field in ('discussion', 'discussed', 'title'):
                if field in proc and isinstance(proc[field], str):
                    proc[field] = sanitize_html(proc[field])

    # Validate
    errors = []
    if not meeting_number:
        errors.append('Numer posiedzenia jest wymagany.')
    if not year:
        errors.append('Rok jest wymagany.')

    # Parse date/time
    meeting_date = None
    if meeting_date_str:
        try:
            meeting_date = datetime.strptime(meeting_date_str, '%Y-%m-%d').date()
        except ValueError:
            errors.append('Nieprawidłowy format daty.')

    start_time = None
    if start_time_str:
        try:
            start_time = datetime.strptime(start_time_str, '%H:%M').time()
        except ValueError:
            errors.append('Nieprawidłowy format godziny rozpoczęcia.')

    end_time = None
    if end_time_str:
        try:
            end_time = datetime.strptime(end_time_str, '%H:%M').time()
        except ValueError:
            errors.append('Nieprawidłowy format godziny zakończenia.')

    if errors:
        for error in errors:
            flash(error, 'error')
        return redirect(request.url)

    try:
        if meeting:
            # Update existing
            meeting.meeting_number = meeting_number
            meeting.year = year
            meeting.meeting_date = meeting_date
            meeting.start_time = start_time
            meeting.end_time = end_time
            meeting.location = location or 'Siedziba Izby'
            meeting.chairperson_id = chairperson_id
            meeting.secretary_id = secretary_id
            meeting.guests = guests or None
            meeting.agenda_items = agenda_items if agenda_items else None
            meeting.attendance = attendance if attendance else None
            meeting.proceedings = proceedings if proceedings else None
            meeting.updated_by = current_user.id
            meeting.updated_at = datetime.now()

            # Update quorum count
            if attendance:
                meeting.quorum_count = sum(1 for a in attendance.values() if a.get('present'))
                meeting.quorum_confirmed = meeting.quorum_count >= 9  # Majority of 16

            db.commit()
            flash(f'Posiedzenie {meeting.meeting_identifier} zostało zaktualizowane.', 'success')
            return redirect(url_for('board.meeting_view', meeting_id=meeting.id))
        else:
            # Create new
            new_meeting = BoardMeeting(
                meeting_number=meeting_number,
                year=year,
                meeting_date=meeting_date,
                start_time=start_time,
                end_time=end_time,
                location=location or 'Siedziba Izby',
                chairperson_id=chairperson_id,
                secretary_id=secretary_id,
                guests=guests or None,
                agenda_items=agenda_items if agenda_items else None,
                attendance=attendance if attendance else None,
                proceedings=proceedings if proceedings else None,
                status=BoardMeeting.STATUS_DRAFT,
                created_by=current_user.id
            )

            if attendance:
                new_meeting.quorum_count = sum(1 for a in attendance.values() if a.get('present'))
                new_meeting.quorum_confirmed = new_meeting.quorum_count >= 9

            db.add(new_meeting)
            db.commit()

            flash(f'Posiedzenie {new_meeting.meeting_identifier} zostało utworzone.', 'success')
            return redirect(url_for('board.meeting_view', meeting_id=new_meeting.id))

    except Exception as e:
        db.rollback()
        current_app.logger.error(f"Failed to save meeting: {e}")
        flash('Błąd podczas zapisywania posiedzenia.', 'error')
        return redirect(request.url)
