#!/usr/bin/env python3
"""
Norda Biznes Partner - Flask Application
====================================

Main Flask application for Norda Biznes company directory with AI chat.

Features:
- User authentication with email confirmation
- Company directory with advanced search
- AI chat assistant powered by Google Gemini
- PostgreSQL database integration
- Analytics dashboard for chat insights

Author: Norda Biznes Development Team
Created: 2025-11-23
"""

import os
import logging
import secrets
import re
import json
import time
from collections import deque
from pathlib import Path
from datetime import datetime, timedelta, date
from flask import Flask, render_template, request, jsonify, redirect, url_for, flash, session, Response, send_file
from flask_login import login_user, logout_user, login_required, current_user
# Note: CSRFProtect, Limiter, LoginManager are imported from extensions.py (line ~250)
from werkzeug.security import generate_password_hash, check_password_hash
from dotenv import load_dotenv
from user_agents import parse as parse_user_agent
import uuid
import traceback as tb_module

# Load environment variables (override any existing env vars)
# Try .env first, then nordabiz_config.txt for production flexibility
import os
if os.path.exists('.env'):
    load_dotenv('.env', override=True)
elif os.path.exists('nordabiz_config.txt'):
    load_dotenv('nordabiz_config.txt', override=True)
else:
    load_dotenv(override=True)

# ============================================================
# GLOBAL CONSTANTS - MARKETING
# ============================================================
# Liczba podmiotów gospodarczych (cel marketingowy Izby NORDA)
# Używana we wszystkich miejscach wyświetlających liczbę firm
COMPANY_COUNT_MARKETING = 150

# ============================================================
# STAGING TEST FEATURES
# ============================================================
# Features currently being tested on staging environment.
# Only rendered when STAGING=true in .env. Edit this dict to update.
STAGING_TEST_FEATURES = {
    'board_module': {
        'name': 'Moduł Rada Izby',
        'description': 'Zarządzanie posiedzeniami, dokumenty, rola OFFICE_MANAGER',
        'nav_item': 'Rada',
    },
}

# Configure logging with in-memory buffer for debug panel
class DebugLogHandler(logging.Handler):
    """Custom handler that stores logs in memory for real-time viewing"""
    def __init__(self, max_logs=500):
        super().__init__()
        self.logs = deque(maxlen=max_logs)

    def emit(self, record):
        log_entry = {
            'timestamp': datetime.now().isoformat(),
            'level': record.levelname,
            'logger': record.name,
            'message': self.format(record),
            'module': record.module,
            'funcName': record.funcName,
            'lineno': record.lineno
        }
        self.logs.append(log_entry)

# Create debug handler
debug_handler = DebugLogHandler(max_logs=500)
debug_handler.setFormatter(logging.Formatter('%(message)s'))

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

# Add debug handler to root logger
logging.getLogger().addHandler(debug_handler)

logger = logging.getLogger(__name__)

# Security logger for fail2ban integration
# Logs to /var/log/nordabiznes/security.log in production
security_logger = logging.getLogger('security')
security_logger.setLevel(logging.WARNING)
_security_log_path = '/var/log/nordabiznes/security.log'
if os.path.exists('/var/log/nordabiznes'):
    _security_handler = logging.FileHandler(_security_log_path)
    _security_handler.setFormatter(logging.Formatter(
        '%(asctime)s [%(levelname)s] %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    ))
    security_logger.addHandler(_security_handler)

# Import database models
from database import (
    init_db,
    SessionLocal,
    User,
    Company,
    Category,
    Service,
    Competency,
    CompanyDigitalMaturity,
    CompanyWebsiteAnalysis,
    CompanyQualityTracking,
    CompanyWebsiteContent,
    CompanyAIInsights,
    CompanyEvent,
    CompanySocialMedia,
    CompanyContact,
    AIChatConversation,
    AIChatMessage,
    AIChatFeedback,
    AIAPICostLog,
    ForumTopic,
    ForumReply,
    ForumAttachment,
    NordaEvent,
    EventAttendee,
    PrivateMessage,
    Classified,
    UserNotification,
    CompanyRecommendation,
    MembershipFee,
    MembershipFeeConfig,
    Person,
    CompanyPerson,
    GBPAudit,
    ITAudit,
    KRSAudit,
    CompanyPKD,
    CompanyFinancialReport,
    UserSession,
    UserBlock,
    PageView,
    UserClick,
    AnalyticsDaily,
    PopularPagesDaily,
    SearchQuery,
    ConversionEvent,
    JSError,
    PopularSearchesDaily,
    HourlyActivity,
    AuditLog,
    SecurityAlert,
    ZOPKNews,
    SystemRole
)

from utils.decorators import role_required

# Import services
import gemini_service
from nordabiz_chat import NordaBizChatEngine
from search_service import search_companies
import krs_api_service
from file_upload_service import FileUploadService

# Security service for audit log, alerting, GeoIP, 2FA
try:
    from security_service import (
        log_audit, create_security_alert, get_client_ip,
        is_ip_allowed, geoip_check, init_security_service,
        generate_totp_secret, get_totp_uri, verify_totp,
        generate_backup_codes, verify_backup_code, requires_2fa
    )
    SECURITY_SERVICE_AVAILABLE = True
except ImportError as e:
    SECURITY_SERVICE_AVAILABLE = False
    logger.warning(f"Security service not available: {e}")

# News service for fetching company news
try:
    from news_service import NewsService, get_news_service, init_news_service
    NEWS_SERVICE_AVAILABLE = True
except ImportError:
    NEWS_SERVICE_AVAILABLE = False
    logger.warning("News service not available")

# SEO audit components for triggering audits via API
import sys
_scripts_path = os.path.join(os.path.dirname(__file__), 'scripts')
if _scripts_path not in sys.path:
    sys.path.insert(0, _scripts_path)

try:
    from seo_audit import SEOAuditor, SEO_AUDIT_VERSION
    SEO_AUDIT_AVAILABLE = True
except ImportError as e:
    SEO_AUDIT_AVAILABLE = False
    logger.warning(f"SEO audit service not available: {e}")

# GBP (Google Business Profile) audit service
try:
    from gbp_audit_service import (
        GBPAuditService,
        audit_company as gbp_audit_company,
        get_company_audit as gbp_get_company_audit,
        fetch_google_business_data as gbp_fetch_google_data
    )
    GBP_AUDIT_AVAILABLE = True
    GBP_AUDIT_VERSION = '1.0'
except ImportError as e:
    GBP_AUDIT_AVAILABLE = False
    GBP_AUDIT_VERSION = None
    logger.warning(f"GBP audit service not available: {e}")

# KRS (Krajowy Rejestr Sądowy) audit service
try:
    from krs_audit_service import parse_krs_pdf, parse_krs_pdf_full
    KRS_AUDIT_AVAILABLE = True
    KRS_AUDIT_VERSION = '1.0'
except ImportError as e:
    KRS_AUDIT_AVAILABLE = False
    KRS_AUDIT_VERSION = None
    logger.warning(f"KRS audit service not available: {e}")

# Initialize Flask app
app = Flask(__name__)

# Security: Require strong SECRET_KEY (no default value allowed)
SECRET_KEY = os.getenv('SECRET_KEY')
if not SECRET_KEY or len(SECRET_KEY) < 32:
    raise ValueError("SECRET_KEY must be set in environment variables and be at least 32 characters long")
app.config['SECRET_KEY'] = SECRET_KEY

app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)

# Security configurations
app.config['WTF_CSRF_ENABLED'] = True
app.config['WTF_CSRF_TIME_LIMIT'] = None  # No time limit for CSRF tokens
app.config['SESSION_COOKIE_SECURE'] = os.getenv('FLASK_ENV') != 'development'  # HTTPS only in production
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'

# Template filters
@app.template_filter('ensure_url')
def ensure_url_filter(url):
    """Ensure URL has http:// or https:// scheme"""
    if url and not url.startswith(('http://', 'https://')):
        return f'https://{url}'
    return url

# Register forum markdown filter
from utils.markdown import register_markdown_filter
register_markdown_filter(app)

# Initialize extensions from centralized extensions.py
from extensions import csrf, limiter, login_manager

csrf.init_app(app)

# Initialize rate limiter with Redis storage (persistent across restarts)
# Falls back to memory if Redis unavailable
_redis_available = False
try:
    import redis
    _redis_client = redis.Redis(host='localhost', port=6379, db=0)
    _redis_client.ping()
    _redis_available = True
    logger.info("Rate limiter using Redis storage")
except Exception:
    logger.warning("Redis unavailable, rate limiter using memory storage")

# Note: default_limits are set in extensions.py
# Here we only configure storage
if _redis_available:
    limiter._storage_uri = "redis://localhost:6379/0"
else:
    limiter._storage_uri = "memory://"
limiter.init_app(app)

@limiter.request_filter
def is_admin_exempt():
    """Exempt logged-in admins from rate limiting."""
    from flask_login import current_user
    try:
        return current_user.is_authenticated and current_user.has_role(SystemRole.ADMIN)
    except Exception:
        return False

# Initialize database
init_db()

# Initialize Login Manager (imported from extensions.py)
login_manager.init_app(app)
login_manager.login_view = 'login'  # Will change to 'auth.login' after full migration
login_manager.login_message = 'Zaloguj się, aby uzyskać dostęp do tej strony.'

# Initialize Gemini service
try:
    gemini_service.init_gemini_service(model='3-flash')  # Paid tier: 10K RPD, thinking mode, fallback: 2.5-flash-lite (Unlimited) → 2.5-flash (10K)
    logger.info("Gemini service initialized successfully")
except Exception as e:
    logger.error(f"Failed to initialize Gemini service: {e}")

# Register blueprints (Phase 1: reports, community)
from blueprints import register_blueprints
register_blueprints(app)
logger.info("Blueprints registered")

@login_manager.user_loader
def load_user(user_id):
    """Load user from database with eager-loaded relationships"""
    from sqlalchemy.orm import joinedload
    db = SessionLocal()
    try:
        user = db.query(User).options(
            joinedload(User.company_associations)
        ).filter_by(id=int(user_id)).first()
        if user:
            # Force-load associations before detaching from session
            _ = user.company_associations
        return user
    finally:
        db.close()


# ============================================================
# TEMPLATE CONTEXT PROCESSORS
# ============================================================

@app.context_processor
def inject_globals():
    """Inject global variables into all templates"""
    is_staging = os.getenv('STAGING') == 'true'
    return {
        'current_year': datetime.now().year,
        'now': datetime.now(),  # Must be value, not method - templates use now.strftime()
        'COMPANY_COUNT': COMPANY_COUNT_MARKETING,  # Liczba podmiotów (cel marketingowy)
        'is_staging': is_staging,
        'staging_features': STAGING_TEST_FEATURES if is_staging else {},
        'SystemRole': SystemRole,
    }


@app.context_processor
def inject_audit_access():
    from utils.decorators import is_audit_owner
    return dict(is_audit_owner=is_audit_owner())


@app.context_processor
def inject_notifications():
    """Inject unread notifications count into all templates"""
    if current_user.is_authenticated:
        db = SessionLocal()
        try:
            unread_count = db.query(UserNotification).filter(
                UserNotification.user_id == current_user.id,
                UserNotification.is_read == False
            ).count()
            return {'unread_notifications_count': unread_count}
        finally:
            db.close()
    return {'unread_notifications_count': 0}


# ============================================================
# NOTIFICATION HELPERS
# ============================================================

def create_notification(user_id, title, message, notification_type='info',
                       related_type=None, related_id=None, action_url=None):
    """
    Create a notification for a user.

    Args:
        user_id: ID of the user to notify
        title: Notification title
        message: Notification message/body
        notification_type: Type of notification (news, system, message, event, alert)
        related_type: Type of related entity (company_news, event, message, etc.)
        related_id: ID of the related entity
        action_url: URL to navigate when notification is clicked

    Returns:
        UserNotification object or None on error
    """
    db = SessionLocal()
    try:
        notification = UserNotification(
            user_id=user_id,
            title=title,
            message=message,
            notification_type=notification_type,
            related_type=related_type,
            related_id=related_id,
            action_url=action_url
        )
        db.add(notification)
        db.commit()
        db.refresh(notification)
        logger.info(f"Created notification for user {user_id}: {title}")
        return notification
    except Exception as e:
        logger.error(f"Error creating notification: {e}")
        db.rollback()
        return None
    finally:
        db.close()


def create_news_notification(company_id, news_id, news_title):
    """
    Create notification for company owner when their news is approved.

    Args:
        company_id: ID of the company
        news_id: ID of the approved news
        news_title: Title of the news
    """
    db = SessionLocal()
    try:
        # Find users associated with this company
        users = db.query(User).filter(
            User.company_id == company_id,
            User.is_active == True
        ).all()

        for user in users:
            create_notification(
                user_id=user.id,
                title="Nowa aktualnosc o Twojej firmie",
                message=f"Aktualnosc '{news_title}' zostala zatwierdzona i jest widoczna na profilu firmy.",
                notification_type='news',
                related_type='company_news',
                related_id=news_id,
                action_url=f"/company/{company_id}"
            )
    finally:
        db.close()


# ============================================================
# USER ANALYTICS - TRACKING HELPERS
# ============================================================

# Global variable to store current page_view_id for templates
_current_page_view_id = {}

def get_or_create_analytics_session():
    """
    Get existing analytics session or create new one.
    Returns the database session ID (integer).
    Includes GeoIP lookup and UTM parameter parsing.
    """
    analytics_session_id = session.get('analytics_session_id')

    if not analytics_session_id:
        analytics_session_id = str(uuid.uuid4())
        session['analytics_session_id'] = analytics_session_id

    db = SessionLocal()
    try:
        user_session = db.query(UserSession).filter_by(session_id=analytics_session_id).first()

        if not user_session:
            # Parse user agent
            ua_string = request.headers.get('User-Agent', '')
            try:
                ua = parse_user_agent(ua_string)
                device_type = 'mobile' if ua.is_mobile else ('tablet' if ua.is_tablet else 'desktop')
                browser = ua.browser.family
                browser_version = ua.browser.version_string
                os_name = ua.os.family
                os_version = ua.os.version_string
            except Exception:
                device_type = 'desktop'
                browser = 'Unknown'
                browser_version = ''
                os_name = 'Unknown'
                os_version = ''

            # GeoIP lookup
            country, city, region = None, None, None
            ip_address = request.headers.get('X-Forwarded-For', request.remote_addr)
            if ip_address:
                ip_address = ip_address.split(',')[0].strip()
            try:
                from security_service import get_geoip_info
                geo_info = get_geoip_info(ip_address)
                if geo_info:
                    country = geo_info.get('country')
                    city = geo_info.get('city')
                    region = geo_info.get('region')
            except Exception as e:
                logger.debug(f"GeoIP lookup failed for {ip_address}: {e}")

            # UTM parameters (z pierwszego requestu sesji)
            utm_source = request.args.get('utm_source', '')[:255] or None
            utm_medium = request.args.get('utm_medium', '')[:255] or None
            utm_campaign = request.args.get('utm_campaign', '')[:255] or None
            utm_term = request.args.get('utm_term', '')[:255] or None
            utm_content = request.args.get('utm_content', '')[:255] or None

            user_session = UserSession(
                session_id=analytics_session_id,
                user_id=current_user.id if current_user.is_authenticated else None,
                ip_address=ip_address,
                user_agent=ua_string[:2000] if ua_string else None,
                device_type=device_type,
                browser=browser[:50] if browser else None,
                browser_version=browser_version[:20] if browser_version else None,
                os=os_name[:50] if os_name else None,
                os_version=os_version[:20] if os_version else None,
                # GeoIP
                country=country,
                city=city,
                region=region,
                # UTM
                utm_source=utm_source,
                utm_medium=utm_medium,
                utm_campaign=utm_campaign,
                utm_term=utm_term,
                utm_content=utm_content
            )
            db.add(user_session)
            db.commit()
            db.refresh(user_session)
        else:
            # Update last activity AND duration
            user_session.last_activity_at = datetime.now()
            user_session.duration_seconds = int(
                (datetime.now() - user_session.started_at).total_seconds()
            )
            if current_user.is_authenticated and not user_session.user_id:
                user_session.user_id = current_user.id
            db.commit()

        return user_session.id
    except Exception as e:
        logger.error(f"Analytics session error: {e}")
        db.rollback()
        return None
    finally:
        db.close()


def track_conversion(event_type: str, company_id: int = None, target_type: str = None,
                     target_value: str = None, metadata: dict = None):
    """
    Track conversion event.

    Args:
        event_type: Type of conversion (register, login, contact_click, rsvp, message, classified)
        company_id: Related company ID (for contact_click)
        target_type: What was clicked (email, phone, website)
        target_value: The value (email address, phone number, etc.)
        metadata: Additional data as dict
    """
    try:
        analytics_session_id = session.get('analytics_session_id')
        session_db_id = None

        db = SessionLocal()
        try:
            if analytics_session_id:
                user_session = db.query(UserSession).filter_by(session_id=analytics_session_id).first()
                if user_session:
                    session_db_id = user_session.id

            # Określ kategorię konwersji
            category_map = {
                'register': 'acquisition',
                'login': 'activation',
                'contact_click': 'engagement',
                'rsvp': 'engagement',
                'message': 'engagement',
                'classified': 'engagement'
            }

            conversion = ConversionEvent(
                session_id=session_db_id,
                user_id=current_user.id if current_user.is_authenticated else None,
                event_type=event_type,
                event_category=category_map.get(event_type, 'other'),
                company_id=company_id,
                target_type=target_type,
                target_value=target_value[:500] if target_value else None,
                source_page=request.url[:500] if request.url else None,
                referrer=request.referrer[:500] if request.referrer else None,
                event_metadata=metadata
            )
            db.add(conversion)
            db.commit()
            logger.info(f"Conversion tracked: {event_type} company={company_id} target={target_type}")
        except Exception as e:
            logger.error(f"Conversion tracking error: {e}")
            db.rollback()
        finally:
            db.close()
    except Exception as e:
        logger.error(f"Conversion tracking outer error: {e}")


@app.before_request
def check_geoip():
    """Block requests from high-risk countries (RU, CN, KP, IR, BY, SY, VE, CU)."""
    # Skip static files and health checks
    if request.path.startswith('/static') or request.path == '/health':
        return

    if not is_ip_allowed():
        ip = request.headers.get('X-Forwarded-For', request.remote_addr)
        if ip:
            ip = ip.split(',')[0].strip()
        from security_service import get_country_code
        country = get_country_code(ip)
        logger.warning(f"GEOIP_BLOCKED ip={ip} country={country} path={request.path}")

        # Create alert for blocked access
        try:
            db = SessionLocal()
            from security_service import create_security_alert
            create_security_alert(
                db, 'geo_blocked', 'low',
                ip_address=ip,
                details={'country': country, 'path': request.path, 'user_agent': request.user_agent.string[:200]}
            )
            db.commit()
            db.close()
        except Exception as e:
            logger.error(f"Failed to create geo block alert: {e}")

        abort(403)


@app.before_request
def track_page_view():
    """Track page views (excluding static files and API calls)"""
    # Skip static files
    if request.path.startswith('/static'):
        return

    # Skip API calls except selected ones
    if request.path.startswith('/api'):
        return

    # Skip analytics tracking endpoints
    if request.path in ['/api/analytics/track', '/api/analytics/heartbeat']:
        return

    # Skip health checks
    if request.path == '/health':
        return

    # Skip favicon
    if request.path == '/favicon.ico':
        return

    try:
        session_db_id = get_or_create_analytics_session()
        if not session_db_id:
            return

        db = SessionLocal()
        try:
            page_view = PageView(
                session_id=session_db_id,
                user_id=current_user.id if current_user.is_authenticated else None,
                url=request.url[:2000] if request.url else '',
                path=request.path[:500] if request.path else '/',
                referrer=request.referrer[:2000] if request.referrer else None
            )

            # Extract company_id from path if on company page
            if request.path.startswith('/company/'):
                try:
                    slug = request.path.split('/')[2].split('?')[0]
                    company = db.query(Company).filter_by(slug=slug).first()
                    if company:
                        page_view.company_id = company.id
                except Exception:
                    pass

            db.add(page_view)

            # Update session page count
            user_session = db.query(UserSession).filter_by(id=session_db_id).first()
            if user_session:
                user_session.page_views_count = (user_session.page_views_count or 0) + 1

            db.commit()

            # Store page_view_id for click tracking (in request context)
            _current_page_view_id[id(request)] = page_view.id

        except Exception as e:
            logger.error(f"Page view tracking error: {e}")
            db.rollback()
        finally:
            db.close()
    except Exception as e:
        logger.error(f"Page view tracking outer error: {e}")


@app.context_processor
def inject_page_view_id():
    """Inject page_view_id into all templates for JS tracking"""
    page_view_id = _current_page_view_id.get(id(request), '')
    return {'page_view_id': page_view_id}


@app.teardown_request
def cleanup_page_view_id(exception=None):
    """Clean up page_view_id from global dict after request"""
    _current_page_view_id.pop(id(request), None)


# ============================================================
# SECURITY MIDDLEWARE & HELPERS
# ============================================================

@app.after_request
def set_security_headers(response):
    """Add security headers to all responses"""
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-Frame-Options'] = 'SAMEORIGIN'
    response.headers['X-XSS-Protection'] = '1; mode=block'
    response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
    response.headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'

    # Content Security Policy
    csp = (
        "default-src 'self'; "
        "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; "
        "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com; "
        "img-src 'self' data: https:; "
        "font-src 'self' https://cdn.jsdelivr.net https://fonts.gstatic.com; "
        "connect-src 'self'"
    )
    response.headers['Content-Security-Policy'] = csp

    return response


def validate_email(email):
    """Validate email format"""
    if not email or len(email) > 255:
        return False

    # RFC 5322 compliant email regex (simplified)
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return re.match(pattern, email) is not None


def validate_password(password):
    """
    Validate password strength

    Requirements:
    - Minimum 8 characters
    - At least one uppercase letter
    - At least one lowercase letter
    - At least one digit
    """
    if not password or len(password) < 8:
        return False, "Hasło musi mieć minimum 8 znaków"

    if not re.search(r'[A-Z]', password):
        return False, "Hasło musi zawierać przynajmniej jedną wielką literę"

    if not re.search(r'[a-z]', password):
        return False, "Hasło musi zawierać przynajmniej jedną małą literę"

    if not re.search(r'\d', password):
        return False, "Hasło musi zawierać przynajmniej jedną cyfrę"

    return True, "OK"


def sanitize_input(text, max_length=1000):
    """Sanitize user input - remove potentially dangerous characters"""
    if not text:
        return ""

    # Remove null bytes
    text = text.replace('\x00', '')

    # Trim to max length
    text = text[:max_length]

    # Strip whitespace
    text = text.strip()

    return text


def get_free_tier_usage():
    """
    Get today's Gemini API usage for free tier tracking.

    Returns:
        Dict with requests_today and tokens_today
    """
    from datetime import date
    from sqlalchemy import func

    db = SessionLocal()
    try:
        today = date.today()
        result = db.query(
            func.count(AIAPICostLog.id).label('requests'),
            func.coalesce(func.sum(AIAPICostLog.total_tokens), 0).label('tokens')
        ).filter(
            func.date(AIAPICostLog.timestamp) == today,
            AIAPICostLog.api_provider == 'gemini'
        ).first()

        return {
            'requests_today': result.requests or 0,
            'tokens_today': int(result.tokens or 0)
        }
    except Exception as e:
        logger.warning(f"Failed to get free tier usage: {e}")
        return {'requests_today': 0, 'tokens_today': 0}
    finally:
        db.close()


def get_brave_api_usage():
    """
    Get Brave Search API usage for current month.

    Brave free tier: 2000 requests/month

    Returns:
        Dict with usage stats and limits
    """
    from datetime import date
    from sqlalchemy import func, extract

    db = SessionLocal()
    try:
        today = date.today()
        current_month = today.month
        current_year = today.year

        # Monthly usage
        monthly_result = db.query(
            func.count(AIAPICostLog.id).label('requests')
        ).filter(
            extract('month', AIAPICostLog.timestamp) == current_month,
            extract('year', AIAPICostLog.timestamp) == current_year,
            AIAPICostLog.api_provider == 'brave'
        ).first()

        # Today's usage
        daily_result = db.query(
            func.count(AIAPICostLog.id).label('requests')
        ).filter(
            func.date(AIAPICostLog.timestamp) == today,
            AIAPICostLog.api_provider == 'brave'
        ).first()

        monthly_used = monthly_result.requests or 0
        daily_used = daily_result.requests or 0
        monthly_limit = 2000  # Brave free tier

        return {
            'requests_today': daily_used,
            'requests_this_month': monthly_used,
            'monthly_limit': monthly_limit,
            'remaining': max(0, monthly_limit - monthly_used),
            'usage_percent': round((monthly_used / monthly_limit) * 100, 1) if monthly_limit > 0 else 0,
            'tier': 'free',
            'is_limit_reached': monthly_used >= monthly_limit
        }
    except Exception as e:
        logger.warning(f"Failed to get Brave API usage: {e}")
        return {
            'requests_today': 0,
            'requests_this_month': 0,
            'monthly_limit': 2000,
            'remaining': 2000,
            'usage_percent': 0,
            'tier': 'free',
            'is_limit_reached': False
        }
    finally:
        db.close()


def log_brave_api_call(user_id=None, feature='news_search', company_name=None):
    """
    Log a Brave API call for usage tracking.

    Args:
        user_id: User who triggered the call (optional)
        feature: Feature name (news_search, etc.)
        company_name: Company being searched (for reference)
    """
    db = SessionLocal()
    try:
        log_entry = AIAPICostLog(
            api_provider='brave',
            model_name='search_api',
            feature=feature,
            user_id=user_id,
            input_tokens=0,
            output_tokens=0,
            total_tokens=0
        )
        db.add(log_entry)
        db.commit()
        logger.debug(f"Logged Brave API call: {feature} for {company_name}")
    except Exception as e:
        logger.error(f"Failed to log Brave API call: {e}")
        db.rollback()
    finally:
        db.close()


# ============================================================
# HEALTH CHECK
# ============================================================

@app.route('/health')
def health():
    """Health check endpoint for monitoring"""
    return {'status': 'ok'}, 200


@app.route('/test-error-500')
@login_required
def test_error_500():
    """Test endpoint to trigger 500 error for notification testing. Admin only."""
    if not current_user.can_access_admin_panel():
        flash('Brak uprawnień', 'error')
        return redirect(url_for('index'))
    # Intentionally raise an error to test error notification
    raise Exception("TEST ERROR 500 - Celowy błąd testowy do sprawdzenia powiadomień email")


@app.route('/health/full')
@login_required
@role_required(SystemRole.ADMIN)
def health_full():
    """
    Extended health check - verifies all critical endpoints.
    Returns detailed status of each endpoint.
    Access: /health/full
    """
    results = []
    all_ok = True

    # List of ALL endpoints to check (path, name)
    # Comprehensive list updated 2026-01-17
    endpoints = [
        # ========== PUBLIC PAGES ==========
        ('/', 'Strona główna'),
        ('/login', 'Logowanie'),
        ('/register', 'Rejestracja'),
        ('/release-notes', 'Historia zmian'),
        ('/search?q=test', 'Wyszukiwarka'),
        ('/aktualnosci', 'Aktualności'),
        ('/forum', 'Forum'),
        ('/kalendarz', 'Kalendarz wydarzeń'),
        ('/tablica', 'Tablica ogłoszeń'),
        ('/nowi-czlonkowie', 'Nowi członkowie'),
        ('/mapa-polaczen', 'Mapa połączeń'),
        ('/forgot-password', 'Reset hasła'),

        # ========== RAPORTY ==========
        ('/raporty/', 'Raporty'),
        ('/raporty/staz-czlonkostwa', 'Raport: Staż członkostwa'),
        ('/raporty/social-media', 'Raport: Social Media'),
        ('/raporty/struktura-branzowa', 'Raport: Struktura branżowa'),

        # ========== ZOPK PUBLIC ==========
        ('/zopk', 'ZOPK: Strona główna'),
        ('/zopk/aktualnosci', 'ZOPK: Aktualności'),

        # ========== CHAT ==========
        ('/chat', 'NordaGPT Chat'),

        # ========== IT AUDIT ==========
        ('/it-audit/form', 'IT Audit: Formularz'),

        # ========== PUBLIC API ==========
        ('/api/companies', 'API: Lista firm'),
        ('/api/model-info', 'API: Model info'),
        ('/api/gbp/audit/health', 'API: GBP health'),

        # ========== ADMIN: CORE ==========
        ('/admin/security', 'Admin: Bezpieczeństwo'),
        ('/admin/analytics', 'Admin: Analityka'),
        ('/admin/status', 'Admin: Status systemu'),
        ('/admin/health', 'Admin: Health dashboard'),
        ('/admin/debug', 'Admin: Debug'),
        ('/admin/ai-usage', 'Admin: AI Usage'),
        ('/admin/chat-analytics', 'Admin: Chat analytics'),
        ('/admin/users', 'Admin: Użytkownicy'),
        ('/admin/recommendations', 'Admin: Rekomendacje'),
        ('/admin/fees', 'Admin: Składki'),

        # ========== ADMIN: AUDITS ==========
        ('/admin/seo', 'Admin: SEO Audit'),
        ('/admin/gbp-audit', 'Admin: GBP Audit'),
        ('/admin/social-media', 'Admin: Social Media'),
        ('/admin/social-audit', 'Admin: Social Audit'),
        ('/admin/it-audit', 'Admin: IT Audit'),
        ('/admin/digital-maturity', 'Admin: Digital Maturity'),
        ('/admin/krs-audit', 'Admin: KRS Audit'),

        # ========== ADMIN: COMMUNITY ==========
        ('/admin/forum', 'Admin: Forum'),
        ('/admin/kalendarz', 'Admin: Kalendarz'),

        # ========== ADMIN: ZOPK ==========
        ('/admin/zopk', 'Admin: ZOPK Panel'),
        ('/admin/zopk/news', 'Admin: ZOPK News'),
        ('/admin/zopk/knowledge', 'Admin: ZOPK Knowledge'),
        ('/admin/zopk/knowledge/chunks', 'Admin: ZOPK Chunks'),
        ('/admin/zopk/knowledge/facts', 'Admin: ZOPK Facts'),
        ('/admin/zopk/knowledge/entities', 'Admin: ZOPK Entities'),
        ('/admin/zopk/knowledge/duplicates', 'Admin: ZOPK Duplikaty'),
        ('/admin/zopk/knowledge/fact-duplicates', 'Admin: ZOPK Fact Duplicates'),
        ('/admin/zopk/knowledge/graph', 'Admin: ZOPK Graf'),
        ('/admin/zopk/timeline', 'Admin: ZOPK Timeline'),

        # ========== ZOPK API ==========
        ('/api/zopk/milestones', 'API: ZOPK Milestones'),
        ('/api/zopk/knowledge/dashboard-stats', 'API: ZOPK Dashboard stats'),

        # ========== USER SETTINGS (v1.19.0) ==========
        ('/settings/privacy', 'Ustawienia: Prywatność'),
        ('/settings/blocks', 'Ustawienia: Blokady'),
        ('/settings/2fa', 'Ustawienia: 2FA'),

        # ========== WIADOMOŚCI ==========
        ('/wiadomosci', 'Wiadomości: Odebrane'),
        ('/wiadomosci/wyslane', 'Wiadomości: Wysłane'),
        ('/wiadomosci/nowa', 'Wiadomości: Nowa'),

        # ========== EDUKACJA ==========
        ('/edukacja', 'Edukacja: Strona główna'),

        # ========== ADMIN: INSIGHTS ==========
        ('/admin/insights', 'Admin: Insights'),
    ]

    # Dodaj losową firmę do sprawdzenia
    db = SessionLocal()
    try:
        random_company = db.query(Company).first()
        if random_company:
            endpoints.append((f'/company/{random_company.slug}', f'Profil: {random_company.name[:25]}'))
    finally:
        db.close()

    # Testuj każdy endpoint używając test client
    with app.test_client() as client:
        for path, name in endpoints:
            try:
                response = client.get(path, follow_redirects=False)
                status = response.status_code

                # 200 = OK, 302 = redirect (np. do logowania) = OK
                # 429 = rate limited (endpoint działa, tylko ograniczony)
                # 500 = błąd serwera, 404 = nie znaleziono
                if status in (200, 302, 304, 429):
                    results.append({
                        'endpoint': path,
                        'name': name,
                        'status': status,
                        'ok': True
                    })
                else:
                    results.append({
                        'endpoint': path,
                        'name': name,
                        'status': status,
                        'ok': False
                    })
                    all_ok = False

            except Exception as e:
                results.append({
                    'endpoint': path,
                    'name': name,
                    'status': 500,
                    'ok': False,
                    'error': str(e)[:100]
                })
                all_ok = False

    # Podsumowanie
    passed = sum(1 for r in results if r['ok'])
    failed = len(results) - passed

    return {
        'status': 'ok' if all_ok else 'degraded',
        'summary': {
            'total': len(results),
            'passed': passed,
            'failed': failed
        },
        'endpoints': results,
        'timestamp': datetime.now().isoformat()
    }, 200 if all_ok else 503


# ============================================================
# PUBLIC ROUTES - MOVED TO blueprints/public/routes.py
# ============================================================
# The routes below have been migrated to the public blueprint.
# They are commented out but preserved for reference.
# See: blueprints/public/routes.py
# ============================================================
# RECOMMENDATIONS ADMIN ROUTES - MOVED TO: blueprints/admin/routes.py
# ============================================================

# ============================================================
# USER MANAGEMENT ADMIN ROUTES
# Moved to: blueprints/admin/routes.py
# NOTE: AI-parse routes remain below
# ============================================================

# admin_users, admin_user_add - MOVED TO: blueprints/admin/routes.py


# AI-ASSISTED USER CREATION - MOVED TO blueprints/admin/routes_users_api.py
# Routes: /admin/users-api/ai-parse, /admin/users-api/bulk-create


# ============================================================
# USER ANALYTICS API ROUTES - MOVED TO blueprints/api/routes_analytics.py
# ============================================================
# Routes: /api/analytics/track, /api/analytics/heartbeat, /api/analytics/scroll,
#         /api/analytics/error, /api/analytics/performance, /api/analytics/conversion


# ============================================================
# RECOMMENDATIONS API ROUTES - MOVED TO blueprints/api/routes_recommendations.py
# ============================================================
# Routes: /api/recommendations/<company_id>, /api/recommendations/create,
#         /api/recommendations/<rec_id>/edit, /api/recommendations/<rec_id>/delete


# ============================================================
# B2B CLASSIFIEDS ROUTES - MIGRATED TO blueprints/community/classifieds/
# ============================================================
# Routes: /tablica, /tablica/nowe, /tablica/<id>, /tablica/<id>/zakoncz

# ============================================================
# NEW MEMBERS ROUTE - MOVED TO blueprints/public/routes.py
# ============================================================
# AUTHENTICATION ROUTES - MOVED TO blueprints/auth/routes.py
# ============================================================
# The routes below have been migrated to the auth blueprint.
# They are commented out but preserved for reference.
# See: blueprints/auth/routes.py
# ============================================================
# TWO-FACTOR AUTHENTICATION - MOVED TO blueprints/auth/routes.py
# ============================================================
# MOJE KONTO - MOVED TO blueprints/auth/routes.py
# ============================================================
# USER DASHBOARD - MOVED TO blueprints/public/routes.py
# ============================================================
# API ROUTES - MOVED TO: blueprints/api/routes_company.py
# Routes: /api/companies, /api/connections, /api/check-email, /api/verify-nip,
#         /api/verify-krs, /api/company/<id>/refresh-krs, /api/company/<id>/enrich-ai,
#         /api/model-info, /api/admin/test-sanitization
# ============================================================

# ============================================================
# SEO/GBP/SOCIAL AUDIT API - MOVED TO: blueprints/api/routes_*_audit.py
# ============================================================

# ============================================================
# AUDIT DASHBOARDS - MOVED TO: blueprints/audit/routes.py
# ============================================================


# Validation and Company API routes moved to blueprints/api/routes_company.py

# ============================================================
# MODEL COMPARISON - Porównanie modeli AI
# ============================================================


# ============================================================
# SYSTEM STATUS DASHBOARD (Admin only)
# MOVED TO blueprints/admin/routes_status.py
# ============================================================


# ============================================================
# DEBUG PANEL (Admin only)
# ============================================================


# ============================================================
# SOCIAL MEDIA AUDIT ADMIN DASHBOARD
# ============================================================


# ============================================================
# IT AUDIT ADMIN DASHBOARD
# ============================================================


# ============================================================
# IT AUDIT FORM - MOVED TO blueprints/it_audit/
# ============================================================
# Routes: /it-audit/form, /it-audit/save, /api/it-audit/*


# ============================================================
# RAPORTY - MIGRATED TO blueprints/reports/
# ============================================================
# Routes: /raporty, /raporty/staz-czlonkostwa, /raporty/social-media, /raporty/struktura-branzowa


# RELEASE NOTES - MOVED TO blueprints/admin/routes.py (admin_notify_release)


# ============================================================
# ============================================================
# ZOPK PUBLIC ROUTES - MOVED TO blueprints/public/routes_zopk.py
# Routes: /zopk, /zopk/projekty/<slug>, /zopk/aktualnosci
# ============================================================


# ============================================================
# ZOPK ROUTES - MOVED TO BLUEPRINTS
# ============================================================
# All ZOPK routes have been migrated to:
# - blueprints/admin/routes_zopk_dashboard.py
# - blueprints/admin/routes_zopk_news.py
# - blueprints/admin/routes_zopk_knowledge.py
# - blueprints/admin/routes_zopk_timeline.py
# ============================================================

# Endpoint aliases for ZOPK are created in blueprints/__init__.py

# ============================================================
# KRS AUDIT (Krajowy Rejestr Sądowy)
# ============================================================


# ============================================================
# KRS API ROUTES - MOVED TO blueprints/admin/routes_krs_api.py
# ============================================================
# Routes: /admin/krs-api/audit, /admin/krs-api/audit/batch, /admin/krs-api/pdf/<company_id>


# ============================================================
# ERROR HANDLERS
# ============================================================

@app.errorhandler(404)
def not_found(error):
    return render_template('errors/404.html'), 404


from flask_wtf.csrf import CSRFError

@app.errorhandler(CSRFError)
def handle_csrf_error(e):
    flash('Sesja wygasła lub formularz został nieprawidłowo przesłany. Spróbuj ponownie.', 'warning')
    return redirect(request.referrer or url_for('index'))


def send_registration_notification(user_info):
    """Send email notification when a new user registers"""
    try:
        from email_service import send_email, is_configured

        if not is_configured():
            logger.warning("Email service not configured - skipping registration notification")
            return

        notify_email = os.getenv('ERROR_NOTIFY_EMAIL', 'maciej.pienczyn@inpi.pl')
        if not notify_email:
            return

        reg_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        is_member = "✅ TAK" if user_info.get('is_norda_member') else "❌ NIE"
        company_name = user_info.get('company_name', 'Brak przypisanej firmy')

        subject = f"👤 NordaBiz: Nowa rejestracja - {user_info.get('name', 'Nieznany')}"

        body_text = f"""👤 NOWA REJESTRACJA NA NORDABIZNES.PL
{'='*50}

🕐 Czas: {reg_time}
👤 Imię: {user_info.get('name', 'N/A')}
📧 Email: {user_info.get('email', 'N/A')}
🏢 NIP: {user_info.get('company_nip', 'N/A')}
🏛️ Firma: {company_name}
🎫 Członek NORDA: {is_member}

{'='*50}
🔗 Panel użytkowników: https://nordabiznes.pl/admin/users
"""

        body_html = f"""<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"></head>
<body style="font-family: 'Inter', Arial, sans-serif; background: #f8fafc; color: #1e293b; padding: 20px;">
    <div style="max-width: 600px; margin: 0 auto;">
        <div style="background: linear-gradient(135deg, #10b981, #059669); color: white; padding: 20px; border-radius: 8px 8px 0 0;">
            <h1 style="margin: 0; font-size: 20px;">👤 Nowa rejestracja na NordaBiznes.pl</h1>
        </div>
        <div style="background: white; padding: 25px; border-radius: 0 0 8px 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
            <table style="width: 100%; border-collapse: collapse;">
                <tr><td style="color: #64748b; padding: 8px 0; border-bottom: 1px solid #e2e8f0;">🕐 Czas:</td><td style="padding: 8px 0; border-bottom: 1px solid #e2e8f0; font-weight: 500;">{reg_time}</td></tr>
                <tr><td style="color: #64748b; padding: 8px 0; border-bottom: 1px solid #e2e8f0;">👤 Imię:</td><td style="padding: 8px 0; border-bottom: 1px solid #e2e8f0; font-weight: 600; color: #1e40af;">{user_info.get('name', 'N/A')}</td></tr>
                <tr><td style="color: #64748b; padding: 8px 0; border-bottom: 1px solid #e2e8f0;">📧 Email:</td><td style="padding: 8px 0; border-bottom: 1px solid #e2e8f0;"><a href="mailto:{user_info.get('email', '')}" style="color: #2563eb;">{user_info.get('email', 'N/A')}</a></td></tr>
                <tr><td style="color: #64748b; padding: 8px 0; border-bottom: 1px solid #e2e8f0;">🏢 NIP:</td><td style="padding: 8px 0; border-bottom: 1px solid #e2e8f0; font-family: monospace;">{user_info.get('company_nip', 'N/A')}</td></tr>
                <tr><td style="color: #64748b; padding: 8px 0; border-bottom: 1px solid #e2e8f0;">🏛️ Firma:</td><td style="padding: 8px 0; border-bottom: 1px solid #e2e8f0;">{company_name}</td></tr>
                <tr><td style="color: #64748b; padding: 8px 0;">🎫 Członek NORDA:</td><td style="padding: 8px 0; font-weight: 600;">{is_member}</td></tr>
            </table>
            <div style="margin-top: 25px; text-align: center;">
                <a href="https://nordabiznes.pl/admin/users" style="display: inline-block; padding: 12px 24px; background: #2563eb; color: white; text-decoration: none; border-radius: 6px; font-weight: 500;">Otwórz panel użytkowników</a>
            </div>
        </div>
    </div>
</body>
</html>"""

        result = send_email(
            to=[notify_email],
            subject=subject,
            body_text=body_text,
            body_html=body_html,
            email_type='registration_notification'
        )

        if result:
            logger.info(f"Registration notification sent to {notify_email}")
        else:
            logger.error(f"Failed to send registration notification to {notify_email}")

    except Exception as e:
        logger.error(f"Failed to send registration notification: {e}")


def send_error_notification(error, request_info):
    """Send email notification about 500 errors via Microsoft Graph"""
    try:
        from email_service import send_email, is_configured

        if not is_configured():
            logger.warning("Email service not configured - skipping error notification")
            return

        error_email = os.getenv('ERROR_NOTIFY_EMAIL', 'maciej.pienczyn@inpi.pl')
        if not error_email:
            return

        # Build error details
        error_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        traceback_str = tb_module.format_exc()

        subject = f"🚨 NordaBiz ERROR 500: {request_info.get('path', 'Unknown')}"

        body_text = f"""⚠️ BŁĄD 500 NA NORDABIZNES.PL
{'='*50}

🕐 Czas: {error_time}
🌐 URL: {request_info.get('url', 'N/A')}
📍 Ścieżka: {request_info.get('path', 'N/A')}
📝 Metoda: {request_info.get('method', 'N/A')}
👤 Użytkownik: {request_info.get('user', 'Anonimowy')}
🖥️ IP: {request_info.get('ip', 'N/A')}
🌍 User-Agent: {request_info.get('user_agent', 'N/A')}

{'='*50}
📋 BŁĄD:
{str(error)}

{'='*50}
📜 TRACEBACK:
{traceback_str}

{'='*50}
🔧 Sprawdź logi: ssh maciejpi@10.22.68.249 "sudo journalctl -u nordabiznes --since '10 minutes ago'"
"""

        body_html = f"""<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"></head>
<body style="font-family: 'Courier New', monospace; background: #1e1e1e; color: #d4d4d4; padding: 20px;">
    <div style="max-width: 800px; margin: 0 auto;">
        <div style="background: #dc2626; color: white; padding: 15px 20px; border-radius: 8px 8px 0 0;">
            <h1 style="margin: 0; font-size: 20px;">🚨 BŁĄD 500 NA NORDABIZNES.PL</h1>
        </div>
        <div style="background: #2d2d2d; padding: 20px; border-radius: 0 0 8px 8px;">
            <table style="width: 100%; border-collapse: collapse;">
                <tr><td style="color: #9ca3af; padding: 5px 0;">🕐 Czas:</td><td style="color: #fbbf24;">{error_time}</td></tr>
                <tr><td style="color: #9ca3af; padding: 5px 0;">🌐 URL:</td><td style="color: #60a5fa; word-break: break-all;">{request_info.get('url', 'N/A')}</td></tr>
                <tr><td style="color: #9ca3af; padding: 5px 0;">📍 Ścieżka:</td><td style="color: #34d399;">{request_info.get('path', 'N/A')}</td></tr>
                <tr><td style="color: #9ca3af; padding: 5px 0;">📝 Metoda:</td><td>{request_info.get('method', 'N/A')}</td></tr>
                <tr><td style="color: #9ca3af; padding: 5px 0;">👤 Użytkownik:</td><td>{request_info.get('user', 'Anonimowy')}</td></tr>
                <tr><td style="color: #9ca3af; padding: 5px 0;">🖥️ IP:</td><td>{request_info.get('ip', 'N/A')}</td></tr>
            </table>
            <div style="margin-top: 20px; padding: 15px; background: #1e1e1e; border-radius: 8px; border-left: 4px solid #dc2626;">
                <div style="color: #f87171; font-weight: bold; margin-bottom: 10px;">📋 BŁĄD:</div>
                <pre style="margin: 0; white-space: pre-wrap; color: #fca5a5;">{str(error)}</pre>
            </div>
            <div style="margin-top: 20px; padding: 15px; background: #1e1e1e; border-radius: 8px; border-left: 4px solid #f59e0b;">
                <div style="color: #fbbf24; font-weight: bold; margin-bottom: 10px;">📜 TRACEBACK:</div>
                <pre style="margin: 0; white-space: pre-wrap; font-size: 12px; color: #9ca3af; max-height: 400px; overflow: auto;">{traceback_str}</pre>
            </div>
            <div style="margin-top: 20px; padding: 15px; background: #1e3a5f; border-radius: 8px;">
                <div style="color: #60a5fa;">🔧 <strong>Sprawdź logi:</strong></div>
                <code style="display: block; margin-top: 10px; color: #34d399; word-break: break-all;">ssh maciejpi@10.22.68.249 "sudo journalctl -u nordabiznes --since '10 minutes ago'"</code>
            </div>
        </div>
    </div>
</body>
</html>"""

        result = send_email(
            to=[error_email],
            subject=subject,
            body_text=body_text,
            body_html=body_html,
            email_type='error_notification'
        )

        if result:
            logger.info(f"Error notification sent to {error_email}")
        else:
            logger.error(f"Failed to send error notification to {error_email}")

    except Exception as e:
        logger.error(f"Failed to send error notification: {e}")


@app.errorhandler(500)
def internal_error(error):
    # Collect request info for notification
    request_info = {
        'url': request.url if request else 'N/A',
        'path': request.path if request else 'N/A',
        'method': request.method if request else 'N/A',
        'ip': request.remote_addr if request else 'N/A',
        'user_agent': request.headers.get('User-Agent', 'N/A') if request else 'N/A',
        'user': current_user.email if current_user and current_user.is_authenticated else 'Anonimowy'
    }

    # Send notification in background (don't block response)
    try:
        send_error_notification(error, request_info)
    except Exception as e:
        logger.error(f"Error notification failed: {e}")

    return render_template('errors/500.html'), 500


# ============================================================
# ADMIN - SECURITY DASHBOARD
# ============================================================


# ============================================================
# ANNOUNCEMENTS (Ogłoszenia dla członków)
# ============================================================

def generate_slug(title):
    """
    Generate URL-friendly slug from title.
    Uses unidecode for proper Polish character handling.
    """
    import re
    try:
        from unidecode import unidecode
        text = unidecode(title.lower())
    except ImportError:
        # Fallback without unidecode
        text = title.lower()
        replacements = {
            'ą': 'a', 'ć': 'c', 'ę': 'e', 'ł': 'l', 'ń': 'n',
            'ó': 'o', 'ś': 's', 'ź': 'z', 'ż': 'z'
        }
        for pl, en in replacements.items():
            text = text.replace(pl, en)

    # Remove special characters, replace spaces with hyphens
    text = re.sub(r'[^\w\s-]', '', text)
    text = re.sub(r'[-\s]+', '-', text).strip('-')
    return text[:200]  # Limit slug length


# ============================================================
# PUBLIC ANNOUNCEMENTS - MOVED TO blueprints/public/routes_announcements.py
# ============================================================
# Routes: /ogloszenia, /ogloszenia/<slug>


# ============================================================
# EXTERNAL CONTACTS - PAGE ROUTES MIGRATED TO blueprints/community/contacts/
# ============================================================
# Routes: /kontakty, /kontakty/<id>, /kontakty/dodaj, /kontakty/<id>/edytuj, /kontakty/<id>/usun
# API routes remain below for backwards compatibility


# ============================================================
# CONTACTS API ROUTES - MOVED TO blueprints/api/routes_contacts.py
# ============================================================
# Routes: /api/contacts/ai-parse, /api/contacts/bulk-create
# Includes AI prompts for contact parsing


# ============================================================
# HONEYPOT ENDPOINTS (trap for malicious bots)
# ============================================================

@app.route('/wp-admin')
@app.route('/wp-admin/<path:path>')
@app.route('/wp-login.php')
@app.route('/administrator')
@app.route('/phpmyadmin')
@app.route('/phpmyadmin/<path:path>')
@app.route('/.env')
@app.route('/.git/config')
@app.route('/xmlrpc.php')
@app.route('/config.php')
@app.route('/admin.php')
def honeypot_trap(path=None):
    """
    Honeypot endpoints - log and return 404.
    These URLs are commonly probed by malicious bots looking for WordPress,
    phpMyAdmin, or exposed configuration files.
    """
    client_ip = request.headers.get('X-Forwarded-For', request.remote_addr)
    if client_ip and ',' in client_ip:
        client_ip = client_ip.split(',')[0].strip()

    security_logger.warning(f"HONEYPOT ip={client_ip} path={request.path} ua={request.user_agent.string[:100]}")

    # Return 404 to not reveal this is a trap
    return render_template('errors/404.html'), 404


# ============================================================
# MAIN
# ============================================================

if __name__ == '__main__':
    # Port 5001 jako domyślny - macOS AirPlay zajmuje 5000
    port = int(os.getenv('PORT', 5001))
    debug = os.getenv('FLASK_ENV') == 'development'

    logger.info(f"Starting Norda Biznes Partner on port {port}")
    app.run(host='0.0.0.0', port=port, debug=debug)
