"""
Membership Routes
==================

Routes for membership application wizard and status tracking.
"""

import logging
from datetime import datetime

from flask import render_template, request, redirect, url_for, flash, jsonify
from flask_login import login_required, current_user
from sqlalchemy.orm.attributes import flag_modified

from . import bp
from database import SessionLocal, MembershipApplication, CompanyDataRequest, Company, UserNotification, User, SystemRole

logger = logging.getLogger(__name__)


# ============================================================
# APPLICATION WIZARD
# ============================================================

@bp.route('/apply')
@login_required
def apply():
    """
    Membership application wizard - multi-step form.
    """
    db = SessionLocal()
    try:
        # Check if user already has a company
        if current_user.company_id:
            flash('Masz już przypisaną firmę. Nie możesz składać nowej deklaracji.', 'warning')
            return redirect(url_for('index'))

        # Check for existing draft or pending application
        existing = db.query(MembershipApplication).filter(
            MembershipApplication.user_id == current_user.id,
            MembershipApplication.status.in_(['draft', 'submitted', 'under_review', 'pending_user_approval', 'changes_requested'])
        ).first()

        if existing:
            # If pending user approval, redirect to review page
            if existing.status == 'pending_user_approval':
                flash('Administrator zaproponował zmiany do Twojej deklaracji. Przejrzyj je i zaakceptuj lub odrzuć.', 'info')
                return redirect(url_for('membership.review_changes', app_id=existing.id))
            # If already submitted, redirect to status page
            if existing.status in ['submitted', 'under_review']:
                flash('Masz już wysłaną deklarację oczekującą na rozpatrzenie.', 'info')
                return redirect(url_for('membership.status'))
            # Otherwise continue editing
            return redirect(url_for('membership.apply_step', step=1))

        # Create new draft
        application = MembershipApplication(
            user_id=current_user.id,
            company_name='',
            nip='',
            email=current_user.email or '',
            status='draft'
        )
        db.add(application)
        db.commit()

        return redirect(url_for('membership.apply_step', step=1))
    finally:
        db.close()


@bp.route('/apply/step/<int:step>', methods=['GET', 'POST'])
@login_required
def apply_step(step):
    """
    Handle specific step of the application wizard.
    Step 1: Company data (from registry)
    Step 2: Additional info
    Step 3: Sections and declaration
    """
    if step not in [1, 2, 3]:
        return redirect(url_for('membership.apply_step', step=1))

    db = SessionLocal()
    try:
        # Get current application
        application = db.query(MembershipApplication).filter(
            MembershipApplication.user_id == current_user.id,
            MembershipApplication.status.in_(['draft', 'changes_requested'])
        ).first()

        if not application:
            # Check if user has submitted application
            submitted = db.query(MembershipApplication).filter(
                MembershipApplication.user_id == current_user.id,
                MembershipApplication.status.in_(['submitted', 'under_review'])
            ).first()
            if submitted:
                flash('Twoja deklaracja oczekuje na rozpatrzenie.', 'info')
                return redirect(url_for('membership.status'))

            # No application at all - redirect to start
            flash('Nie znaleziono aktywnej deklaracji.', 'error')
            return redirect(url_for('membership.status'))

        if request.method == 'POST':
            action = request.form.get('action', 'save')

            if step == 1:
                # Save step 1 data
                application.company_name = request.form.get('company_name', '').strip()
                application.nip = request.form.get('nip', '').strip().replace('-', '').replace(' ', '')
                application.address_postal_code = request.form.get('address_postal_code', '').strip()
                application.address_city = request.form.get('address_city', '').strip()
                application.address_street = request.form.get('address_street', '').strip()
                application.address_number = request.form.get('address_number', '').strip()
                application.delegate_1 = request.form.get('delegate_1', '').strip()
                application.delegate_2 = request.form.get('delegate_2', '').strip()
                application.delegate_3 = request.form.get('delegate_3', '').strip()
                application.krs_number = request.form.get('krs_number', '').strip()
                application.regon = request.form.get('regon', '').strip()
                application.registry_source = request.form.get('registry_source', 'manual')

            elif step == 2:
                # Save step 2 data
                application.website = request.form.get('website', '').strip()
                application.email = request.form.get('email', '').strip()
                application.phone = request.form.get('phone', '').strip()
                application.short_name = request.form.get('short_name', '').strip()
                application.description = request.form.get('description', '').strip()

                founded_date = request.form.get('founded_date', '').strip()
                if founded_date:
                    try:
                        application.founded_date = datetime.strptime(founded_date, '%Y-%m-%d').date()
                    except ValueError:
                        pass

                employee_count = request.form.get('employee_count', '').strip()
                if employee_count:
                    try:
                        application.employee_count = int(employee_count)
                    except ValueError:
                        pass

                application.show_employee_count = request.form.get('show_employee_count') == 'on'
                application.annual_revenue = request.form.get('annual_revenue', '').strip()

                # Related companies (JSON array)
                related = []
                for i in range(1, 6):
                    company = request.form.get(f'related_company_{i}', '').strip()
                    if company:
                        related.append(company)
                application.related_companies = related if related else None

            elif step == 3:
                # Save step 3 data
                sections = request.form.getlist('sections')
                application.sections = sections if sections else []
                application.sections_other = request.form.get('sections_other', '').strip()
                application.business_type = request.form.get('business_type', 'sp_z_oo')
                application.business_type_other = request.form.get('business_type_other', '').strip()

                # GDPR consents
                application.consent_email = request.form.get('consent_email') == 'on'
                application.consent_email_address = request.form.get('consent_email_address', '').strip()
                application.consent_sms = request.form.get('consent_sms') == 'on'
                application.consent_sms_phone = request.form.get('consent_sms_phone', '').strip()

                # Declaration
                application.declaration_accepted = request.form.get('declaration_accepted') == 'on'
                if application.declaration_accepted:
                    application.declaration_accepted_at = datetime.now()
                    application.declaration_ip_address = request.remote_addr

            application.updated_at = datetime.now()
            db.commit()

            if action == 'next':
                if step < 3:
                    return redirect(url_for('membership.apply_step', step=step + 1))
                else:
                    # Final validation before submit
                    errors = validate_application(application)
                    if errors:
                        for error in errors:
                            flash(error, 'error')
                        return redirect(url_for('membership.apply_step', step=3))

                    # Submit application
                    application.status = 'submitted'
                    application.submitted_at = datetime.now()
                    db.commit()

                    # Wyślij notyfikacje do administratorów i kierowników biura
                    try:
                        admin_users = db.query(User).filter(
                            User.system_role.in_([SystemRole.ADMIN.value, SystemRole.OFFICE_MANAGER.value])
                        ).all()

                        for admin in admin_users:
                            notification = UserNotification(
                                user_id=admin.id,
                                title='Nowa deklaracja członkowska',
                                message=f'{current_user.name or current_user.email} złożył/a deklarację członkowską dla firmy "{application.company_name}".',
                                notification_type='alert',
                                related_type='membership_application',
                                related_id=application.id,
                                action_url=url_for('admin.membership_detail', app_id=application.id)
                            )
                            db.add(notification)

                        db.commit()
                        logger.info(f"Notifications sent to {len(admin_users)} admins for membership application {application.id}")
                    except Exception as e:
                        logger.error(f"Failed to send notifications for membership application: {e}")
                        # Nie przerywaj flow - deklaracja już została wysłana

                    flash('Deklaracja została wysłana do rozpatrzenia!', 'success')
                    logger.info(f"Membership application submitted: user={current_user.id}, app={application.id}")
                    return redirect(url_for('membership.status'))

            elif action == 'prev' and step > 1:
                return redirect(url_for('membership.apply_step', step=step - 1))
            else:
                flash('Zapisano zmiany.', 'success')
                return redirect(url_for('membership.apply_step', step=step))

        # GET - render step
        return render_template(
            'membership/apply.html',
            application=application,
            step=step,
            section_choices=MembershipApplication.SECTION_CHOICES,
            business_type_choices=MembershipApplication.BUSINESS_TYPE_CHOICES
        )
    finally:
        db.close()


def validate_application(app):
    """Validate application before submission."""
    errors = []

    if not app.company_name:
        errors.append('Nazwa firmy jest wymagana.')
    if not app.nip or len(app.nip) != 10:
        errors.append('NIP jest wymagany (10 cyfr).')
    if not app.email:
        errors.append('Email jest wymagany.')
    if not app.delegate_1:
        errors.append('Przynajmniej jeden delegat jest wymagany.')
    if not app.sections:
        errors.append('Wybierz przynajmniej jedną sekcję tematyczną.')
    if not app.consent_email:
        errors.append('Zgoda na kontakt email jest wymagana.')
    if not app.declaration_accepted:
        errors.append('Musisz zaakceptować oświadczenie.')

    return errors


# ============================================================
# APPLICATION STATUS
# ============================================================

@bp.route('/status')
@login_required
def status():
    """
    Show current application status.
    """
    db = SessionLocal()
    try:
        applications = db.query(MembershipApplication).filter(
            MembershipApplication.user_id == current_user.id
        ).order_by(MembershipApplication.created_at.desc()).all()

        return render_template('membership/status.html', applications=applications)
    finally:
        db.close()


# ============================================================
# PROPOSED CHANGES REVIEW (User approval workflow)
# ============================================================

@bp.route('/review-changes/<int:app_id>')
@login_required
def review_changes(app_id):
    """
    Page for user to review proposed changes from admin.
    """
    db = SessionLocal()
    try:
        application = db.query(MembershipApplication).get(app_id)

        if not application:
            flash('Nie znaleziono deklaracji.', 'error')
            return redirect(url_for('membership.status'))

        # Verify ownership
        if application.user_id != current_user.id:
            flash('Brak dostępu do tej deklaracji.', 'error')
            return redirect(url_for('membership.status'))

        # Check status
        if application.status != 'pending_user_approval':
            flash('Ta deklaracja nie wymaga przeglądu zmian.', 'info')
            return redirect(url_for('membership.status'))

        # Get reviewer info (person who proposed changes)
        reviewer = None
        if application.proposed_changes_by_id:
            reviewer = db.query(User).get(application.proposed_changes_by_id)

        return render_template(
            'membership/review_changes.html',
            application=application,
            reviewer=reviewer
        )
    finally:
        db.close()


@bp.route('/review-changes/<int:app_id>/accept', methods=['POST'])
@login_required
def accept_changes(app_id):
    """
    User accepts proposed changes from admin.
    Changes are applied and application returns to under_review.
    """
    db = SessionLocal()
    try:
        application = db.query(MembershipApplication).get(app_id)

        if not application:
            return jsonify({'success': False, 'error': 'Nie znaleziono deklaracji'}), 404

        if application.user_id != current_user.id:
            return jsonify({'success': False, 'error': 'Brak dostępu'}), 403

        if application.status != 'pending_user_approval':
            return jsonify({
                'success': False,
                'error': 'Ta deklaracja nie oczekuje na akceptację zmian'
            }), 400

        proposed = application.proposed_changes or {}

        # Get proposer info for history
        proposer = db.query(User).get(application.proposed_changes_by_id) if application.proposed_changes_by_id else None
        proposer_name = proposer.name if proposer else 'Biuro Izby NORDA'

        # Apply all proposed changes
        applied_fields = []
        changes_summary = []
        for field_name, change in proposed.items():
            new_value = change.get('new')
            if new_value is not None:
                if field_name == 'founded_date':
                    try:
                        setattr(application, field_name, datetime.strptime(new_value, '%Y-%m-%d').date())
                    except (ValueError, TypeError):
                        setattr(application, field_name, None)
                else:
                    setattr(application, field_name, new_value)
                applied_fields.append(field_name)
                changes_summary.append(f"{change.get('label', field_name)}: {change.get('old', '-')} → {new_value}")

        # Record user acceptance
        application.user_changes_accepted_at = datetime.now()
        application.user_changes_action = 'accepted'

        # Add to workflow history
        history = application.workflow_history or []
        history.append({
            'event': 'user_accepted_changes',
            'timestamp': datetime.now().isoformat(),
            'user_id': current_user.id,
            'user_name': current_user.name or current_user.email,
            'details': {
                'proposed_by': proposer_name,
                'proposed_at': application.proposed_changes_at.isoformat() if application.proposed_changes_at else None,
                'changes_applied': changes_summary,
                'comment': application.proposed_changes_comment
            }
        })
        application.workflow_history = list(history)  # Create new list for SQLAlchemy change detection
        flag_modified(application, 'workflow_history')

        # Update status - back to under_review for final approval
        application.status = 'under_review'
        application.updated_at = datetime.now()

        # Keep proposed_changes_by_id for reference but clear the rest
        application.proposed_changes = None
        application.proposed_changes_at = None
        application.proposed_changes_comment = None

        # Create notification for admins
        admins = db.query(User).filter(User.role == 'ADMIN').all()
        for admin in admins:
            notification = UserNotification(
                user_id=admin.id,
                title='Użytkownik zaakceptował zmiany',
                message=f'Użytkownik {current_user.name or current_user.email} zaakceptował proponowane zmiany dla firmy "{application.company_name}". Deklaracja oczekuje na ostateczne zatwierdzenie.',
                notification_type='alert',
                related_type='membership_application',
                related_id=app_id,
                action_url=f'/admin/membership/{app_id}'
            )
            db.add(notification)

        db.commit()

        logger.info(
            f"User {current_user.id} accepted proposed changes for application {app_id}. "
            f"Applied fields: {applied_fields}"
        )

        return jsonify({
            'success': True,
            'applied_fields': applied_fields,
            'message': 'Zmiany zostały zaakceptowane. Twoja deklaracja wróciła do rozpatrzenia.'
        })
    except Exception as e:
        db.rollback()
        logger.error(f"Error accepting changes for application {app_id}: {e}")
        return jsonify({'success': False, 'error': str(e)}), 500
    finally:
        db.close()


@bp.route('/review-changes/<int:app_id>/reject', methods=['POST'])
@login_required
def reject_changes(app_id):
    """
    User rejects proposed changes from admin.
    Application returns to under_review with original data.
    """
    db = SessionLocal()
    try:
        application = db.query(MembershipApplication).get(app_id)

        if not application:
            return jsonify({'success': False, 'error': 'Nie znaleziono deklaracji'}), 404

        if application.user_id != current_user.id:
            return jsonify({'success': False, 'error': 'Brak dostępu'}), 403

        if application.status != 'pending_user_approval':
            return jsonify({
                'success': False,
                'error': 'Ta deklaracja nie oczekuje na akceptację zmian'
            }), 400

        data = request.get_json() or {}
        user_comment = data.get('comment', '').strip()

        # Get proposer info for history
        proposer = db.query(User).get(application.proposed_changes_by_id) if application.proposed_changes_by_id else None
        proposer_name = proposer.name if proposer else 'Biuro Izby NORDA'

        # Record user rejection
        application.user_changes_accepted_at = datetime.now()
        application.user_changes_action = 'rejected'

        # Add to workflow history
        history = application.workflow_history or []
        history.append({
            'event': 'user_rejected_changes',
            'timestamp': datetime.now().isoformat(),
            'user_id': current_user.id,
            'user_name': current_user.name or current_user.email,
            'details': {
                'proposed_by': proposer_name,
                'proposed_at': application.proposed_changes_at.isoformat() if application.proposed_changes_at else None,
                'user_comment': user_comment,
                'original_comment': application.proposed_changes_comment
            }
        })
        application.workflow_history = list(history)  # Create new list for SQLAlchemy change detection
        flag_modified(application, 'workflow_history')

        # Clear proposed changes - keep original data
        application.proposed_changes = None
        application.proposed_changes_at = None
        application.proposed_changes_by_id = None
        application.proposed_changes_comment = None

        # Add user's rejection reason to review_comment
        if user_comment:
            existing_comment = application.review_comment or ''
            application.review_comment = f"{existing_comment}\n\n[Użytkownik odrzucił propozycje zmian: {user_comment}]".strip()

        # Back to under_review with original data
        application.status = 'under_review'
        application.updated_at = datetime.now()

        # Create notification for admins
        admins = db.query(User).filter(User.role == 'ADMIN').all()
        for admin in admins:
            notification = UserNotification(
                user_id=admin.id,
                title='Użytkownik odrzucił zmiany',
                message=f'Użytkownik {current_user.name or current_user.email} odrzucił proponowane zmiany dla firmy "{application.company_name}".{" Powód: " + user_comment if user_comment else ""} Deklaracja wraca do rozpatrzenia z oryginalnymi danymi.',
                notification_type='alert',
                related_type='membership_application',
                related_id=app_id,
                action_url=f'/admin/membership/{app_id}'
            )
            db.add(notification)

        db.commit()

        logger.info(
            f"User {current_user.id} rejected proposed changes for application {app_id}. "
            f"Reason: {user_comment or 'brak'}"
        )

        return jsonify({
            'success': True,
            'message': 'Odrzuciłeś proponowane zmiany. Twoja deklaracja zachowuje oryginalne dane.'
        })
    except Exception as e:
        db.rollback()
        logger.error(f"Error rejecting changes for application {app_id}: {e}")
        return jsonify({'success': False, 'error': str(e)}), 500
    finally:
        db.close()


# ============================================================
# COMPANY DATA REQUEST (Modal-based)
# ============================================================

@bp.route('/data-request', methods=['GET', 'POST'])
@login_required
def data_request():
    """
    Handle company data update request.
    For users with a company but missing NIP/data.
    """
    if not current_user.company_id:
        flash('Nie masz przypisanej firmy.', 'warning')
        return redirect(url_for('membership.apply'))

    db = SessionLocal()
    try:
        company = db.query(Company).get(current_user.company_id)
        if not company:
            flash('Firma nie została znaleziona.', 'error')
            return redirect(url_for('index'))

        # Check for pending request
        pending = db.query(CompanyDataRequest).filter(
            CompanyDataRequest.user_id == current_user.id,
            CompanyDataRequest.company_id == company.id,
            CompanyDataRequest.status == 'pending'
        ).first()

        if request.method == 'POST':
            if pending:
                flash('Masz już oczekujące zgłoszenie.', 'warning')
                return redirect(url_for('membership.data_request'))

            nip = request.form.get('nip', '').strip().replace('-', '').replace(' ', '')
            if not nip or len(nip) != 10:
                flash('NIP musi mieć 10 cyfr.', 'error')
                return redirect(url_for('membership.data_request'))

            # Create request
            data_request = CompanyDataRequest(
                request_type='update_data',
                user_id=current_user.id,
                company_id=company.id,
                nip=nip,
                user_note=request.form.get('user_note', '').strip()
            )

            # Try to fetch registry data
            fetched = fetch_registry_data(nip)
            if fetched:
                data_request.registry_source = fetched.get('source')
                data_request.fetched_data = fetched

            db.add(data_request)
            db.commit()

            flash('Zgłoszenie zostało wysłane do rozpatrzenia.', 'success')
            logger.info(f"Company data request created: user={current_user.id}, company={company.id}")
            return redirect(url_for('index'))

        return render_template(
            'membership/data_request.html',
            company=company,
            pending_request=pending
        )
    finally:
        db.close()


def fetch_registry_data(nip):
    """
    Fetch company data from KRS/CEIDG registries.
    Returns dict with source and data, or None if not found.
    """
    try:
        # Try KRS first
        from krs_api_service import krs_api_service
        krs_data = krs_api_service.search_by_nip(nip)
        if krs_data:
            return {
                'source': 'KRS',
                'name': krs_data.get('nazwa'),
                'krs': krs_data.get('krs'),
                'regon': krs_data.get('regon'),
                'address': krs_data.get('adres'),
                'founding_date': krs_data.get('data_rejestracji'),
                'raw': krs_data
            }
    except Exception as e:
        logger.warning(f"KRS lookup failed for NIP {nip}: {e}")

    try:
        # Try CEIDG
        from ceidg_api_service import fetch_ceidg_by_nip
        ceidg_data = fetch_ceidg_by_nip(nip)
        if ceidg_data:
            return {
                'source': 'CEIDG',
                'name': ceidg_data.get('firma'),
                'regon': ceidg_data.get('regon'),
                'address': ceidg_data.get('adres'),
                'raw': ceidg_data
            }
    except Exception as e:
        logger.warning(f"CEIDG lookup failed for NIP {nip}: {e}")

    return None
