"""
Public Routes
=============

Public-facing routes: index, company profiles, search, events, new members,
connections map, release notes, dashboard.
"""

import logging
from datetime import datetime, timedelta, date

from flask import render_template, request, redirect, url_for, flash, session, current_app
from flask_login import login_required, current_user
from sqlalchemy import or_, func

from . import bp
from database import (
    SessionLocal,
    Company,
    Category,
    User,
    CompanyRecommendation,
    CompanyEvent,
    CompanyDigitalMaturity,
    CompanyWebsiteAnalysis,
    CompanyQualityTracking,
    CompanyWebsiteContent,
    CompanyAIInsights,
    CompanySocialMedia,
    CompanyContact,
    Person,
    CompanyPerson,
    GBPAudit,
    ITAudit,
    CompanyPKD,
    NordaEvent,
    EventAttendee,
    AIChatConversation,
    AIChatMessage,
    UserSession,
    SearchQuery,
    MembershipApplication,
    Announcement,
    ForumTopic,
    Classified,
    UserNotification,
    UserCompany,
)
from utils.helpers import sanitize_input
from extensions import limiter
from search_service import search_companies

# Logger
logger = logging.getLogger(__name__)

# Global constant (same as in app.py)
COMPANY_COUNT_MARKETING = 150


@bp.route('/')
def index():
    """Homepage - landing page for guests, company directory for logged in users"""
    if not current_user.is_authenticated:
        # Landing page for guests
        db = SessionLocal()
        try:
            total_companies = db.query(Company).filter_by(status='active').count()
            total_categories = db.query(Category).count()
            return render_template(
                'landing.html',
                total_companies=total_companies,
                total_categories=total_categories
            )
        finally:
            db.close()

    # Company directory for logged in users
    db = SessionLocal()
    try:
        from datetime import date
        companies = db.query(Company).filter_by(status='active').order_by(Company.name).all()

        # Get hierarchical categories (main categories with subcategories)
        main_categories = db.query(Category).filter(
            Category.parent_id.is_(None)
        ).order_by(Category.display_order, Category.name).all()

        # All categories for backwards compatibility
        categories = db.query(Category).order_by(Category.sort_order).all()

        total_companies = len(companies)
        total_categories = len([c for c in categories if db.query(Company).filter_by(category_id=c.id).count() > 0])

        # Najbliższe wydarzenia (dla bannera "Kto weźmie udział?")
        all_upcoming = db.query(NordaEvent).filter(
            NordaEvent.event_date >= date.today()
        ).order_by(NordaEvent.event_date.asc()).all()

        upcoming_events = []
        for event in all_upcoming:
            if event.can_user_view(current_user):
                registered = db.query(EventAttendee).filter(
                    EventAttendee.event_id == event.id,
                    EventAttendee.user_id == current_user.id
                ).first() is not None
                can_attend = event.can_user_attend(current_user)
                upcoming_events.append({
                    'event': event,
                    'user_registered': registered,
                    'user_can_attend': can_attend,
                })
                if len(upcoming_events) >= 3:
                    break

        # Backward compat — next_event used by other parts
        next_event = upcoming_events[0]['event'] if upcoming_events else None

        # ZOPK Knowledge facts — admin only widget
        zopk_facts = []
        if current_user.is_admin:
            try:
                from database import ZOPKKnowledgeFact, ZOPKNews
                zopk_facts = db.query(ZOPKKnowledgeFact).join(ZOPKNews).filter(
                    ZOPKKnowledgeFact.confidence_score >= 0.5
                ).order_by(func.random()).limit(3).all()
            except Exception:
                pass

        # Sprawdź czy użytkownik ma deklarację członkowską w toku
        pending_application = None
        if not current_user.is_norda_member and not current_user.company_id:
            pending_application = db.query(MembershipApplication).filter(
                MembershipApplication.user_id == current_user.id,
                MembershipApplication.status.in_(['draft', 'submitted', 'under_review', 'pending_user_approval', 'changes_requested'])
            ).first()

        return render_template(
            'index.html',
            companies=companies,
            categories=categories,
            main_categories=main_categories,
            total_companies=total_companies,
            total_categories=total_categories,
            next_event=next_event,
            upcoming_events=upcoming_events,
            pending_application=pending_application,
            zopk_facts=zopk_facts
        )
    finally:
        db.close()


@bp.route('/company/<int:company_id>')
def company_detail(company_id):
    """Company detail page - requires login and NORDA membership"""
    # Sprawdź czy użytkownik jest zalogowany
    if not current_user.is_authenticated:
        flash('Zaloguj się, aby zobaczyć szczegóły firmy.', 'warning')
        return redirect(url_for('auth.login'))

    # Sprawdź czy użytkownik jest członkiem NORDA (ma firmę lub flagę is_norda_member)
    if not current_user.is_norda_member and not current_user.company_id:
        flash('Dostęp do katalog firm jest dostępny tylko dla członków Izby NORDA. Złóż deklarację członkowską, aby uzyskać pełny dostęp.', 'info')
        return redirect(url_for('membership.apply'))

    db = SessionLocal()
    try:
        company = db.query(Company).filter_by(id=company_id).first()
        if not company:
            flash('Firma nie znaleziona.', 'error')
            return redirect(url_for('index'))

        # Load digital maturity data if available
        maturity_data = db.query(CompanyDigitalMaturity).filter_by(company_id=company_id).first()
        # Get all website analyses sorted by audit date (latest first)
        all_analyses = db.query(CompanyWebsiteAnalysis).filter_by(
            company_id=company_id
        ).order_by(CompanyWebsiteAnalysis.seo_audited_at.desc()).all()

        # Build dict: company_website_id → analysis (latest first wins)
        analyses_by_website = {}
        website_analysis = None  # backward compat: keep "main" analysis
        for a in all_analyses:
            if a.company_website_id and a.company_website_id not in analyses_by_website:
                analyses_by_website[a.company_website_id] = a
            if website_analysis is None:
                website_analysis = a  # first = latest = main analysis

        # Load quality tracking data
        quality_data = db.query(CompanyQualityTracking).filter_by(company_id=company_id).first()

        # Load company events (latest 10)
        events = db.query(CompanyEvent).filter_by(company_id=company_id).order_by(
            CompanyEvent.event_date.desc(),
            CompanyEvent.created_at.desc()
        ).limit(10).all()

        # Load website scraping data (most recent)
        website_content = db.query(CompanyWebsiteContent).filter_by(company_id=company_id).order_by(
            CompanyWebsiteContent.scraped_at.desc()
        ).first()

        # Load AI insights
        ai_insights = db.query(CompanyAIInsights).filter_by(company_id=company_id).first()

        # Load social media profiles
        social_media = db.query(CompanySocialMedia).filter_by(company_id=company_id).all()

        # Load company contacts (phones, emails with sources)
        contacts = db.query(CompanyContact).filter_by(company_id=company_id).order_by(
            CompanyContact.contact_type,
            CompanyContact.is_primary.desc()
        ).all()

        # Load recommendations (approved only, with recommender details)
        recommendations = db.query(CompanyRecommendation).filter_by(
            company_id=company_id,
            status='approved'
        ).join(User, CompanyRecommendation.user_id == User.id).order_by(
            CompanyRecommendation.created_at.desc()
        ).all()

        # Load people connected to company (zarząd, wspólnicy, prokurenci)
        people = db.query(CompanyPerson).filter_by(
            company_id=company_id
        ).join(Person, CompanyPerson.person_id == Person.id).order_by(
            CompanyPerson.role_category,
            Person.nazwisko
        ).all()

        # Load GBP audit (most recent)
        gbp_audit = db.query(GBPAudit).filter_by(
            company_id=company_id
        ).order_by(GBPAudit.audit_date.desc()).first()

        # Load IT audit (most recent)
        it_audit = db.query(ITAudit).filter_by(
            company_id=company_id
        ).order_by(ITAudit.audit_date.desc()).first()

        # Load PKD codes (all - primary first)
        pkd_codes = db.query(CompanyPKD).filter_by(
            company_id=company_id
        ).order_by(CompanyPKD.is_primary.desc(), CompanyPKD.pkd_code).all()

        # Check if current user can enrich company data (user with company edit rights)
        can_enrich = False
        can_edit_profile = False
        company_managers = []
        if current_user.is_authenticated:
            can_enrich = current_user.can_edit_company(company.id)
            can_edit_profile = current_user.can_manage_company(company.id)

            # If user is a member but not manager, load managers for contact modal
            if not can_edit_profile:
                is_company_member = any(
                    assoc.company_id == company.id
                    for assoc in (current_user.company_associations or [])
                ) or current_user.company_id == company.id

                if is_company_member:
                    company_managers = db.query(User).join(UserCompany).filter(
                        UserCompany.company_id == company.id,
                        UserCompany.role == 'MANAGER'
                    ).all()
                    for m in company_managers:
                        _ = m.name, m.email  # force-load before session close

        # For child brands — inherit NIP from parent for display/enrichment
        effective_nip = company.nip
        if not effective_nip and company.parent_company_id:
            parent = db.query(Company).filter_by(id=company.parent_company_id).first()
            if parent:
                effective_nip = parent.nip

        return render_template('company_detail.html',
            company=company,
            company_id=company.id,  # For analytics conversion tracking
            effective_nip=effective_nip,
            maturity_data=maturity_data,
            website_analysis=website_analysis,
            analyses_by_website=analyses_by_website,
            quality_data=quality_data,
            events=events,
            website_content=website_content,
            ai_insights=ai_insights,
            social_media=social_media,
            contacts=contacts,
            recommendations=recommendations,
            people=people,
            gbp_audit=gbp_audit,
            it_audit=it_audit,
            pkd_codes=pkd_codes,
            can_enrich=can_enrich,
            can_edit_profile=can_edit_profile,
            company_managers=company_managers,
            is_admin=current_user.is_authenticated and current_user.is_admin
        )
    finally:
        db.close()


@bp.route('/company/<slug>')
def company_detail_by_slug(slug):
    """Company detail page by slug - requires login"""
    db = SessionLocal()
    try:
        company = db.query(Company).filter_by(slug=slug).first()
        if not company:
            flash('Firma nie znaleziona.', 'error')
            return redirect(url_for('index'))
        # Redirect to canonical int ID route
        return redirect(url_for('company_detail', company_id=company.id))
    finally:
        db.close()


@bp.route('/osoba/<int:person_id>')
def person_detail(person_id):
    """Person detail page - shows registry data and portal data if available"""
    db = SessionLocal()
    try:
        # Get person with their company relationships
        person = db.query(Person).filter_by(id=person_id).first()
        if not person:
            flash('Osoba nie znaleziona.', 'error')
            return redirect(url_for('index'))

        # Get company roles with company details (only active companies)
        company_roles = db.query(CompanyPerson).filter_by(
            person_id=person_id
        ).join(Company, CompanyPerson.company_id == Company.id).filter(
            Company.status == 'active'
        ).order_by(
            CompanyPerson.role_category,
            Company.name
        ).all()

        # Try to find matching user account by name (for portal data)
        # This is a simple match - in production might need more sophisticated matching
        portal_user = None
        name_parts = person.full_name().upper().split()
        if len(name_parts) >= 2:
            # Try to find user where first/last name matches
            potential_users = db.query(User).filter(
                User.name.isnot(None)
            ).all()
            for u in potential_users:
                if u.name:
                    user_name_parts = u.name.upper().split()
                    # Check if at least first and last name match
                    if len(user_name_parts) >= 2:
                        if (user_name_parts[-1] in name_parts and  # Last name match
                            any(part in user_name_parts for part in name_parts[:-1])):  # First name match
                            portal_user = u
                            break

        return render_template('person_detail.html',
            person=person,
            company_roles=company_roles,
            portal_user=portal_user
        )
    finally:
        db.close()


@bp.route('/company/<slug>/recommend', methods=['GET', 'POST'])
def company_recommend(slug):
    """Create recommendation for a company - requires login"""
    db = SessionLocal()
    try:
        # Get company
        company = db.query(Company).filter_by(slug=slug).first()
        if not company:
            flash('Firma nie znaleziona.', 'error')
            return redirect(url_for('index'))

        # Handle POST (form submission)
        if request.method == 'POST':
            recommendation_text = request.form.get('recommendation_text', '').strip()
            service_category = sanitize_input(request.form.get('service_category', ''), 200)
            show_contact = request.form.get('show_contact') == '1'

            # Validation
            if not recommendation_text or len(recommendation_text) < 50:
                flash('Rekomendacja musi mieć co najmniej 50 znaków.', 'error')
                return render_template('company/recommend.html', company=company)

            if len(recommendation_text) > 2000:
                flash('Rekomendacja może mieć maksymalnie 2000 znaków.', 'error')
                return render_template('company/recommend.html', company=company)

            # Prevent self-recommendation
            if current_user.company_id == company.id:
                flash('Nie możesz polecać własnej firmy.', 'error')
                return redirect(url_for('company_detail', company_id=company.id))

            # Check for duplicate (user already recommended this company)
            existing = db.query(CompanyRecommendation).filter_by(
                user_id=current_user.id,
                company_id=company.id
            ).first()

            if existing:
                flash('Już poleciłeś tę firmę. Możesz edytować swoją wcześniejszą rekomendację.', 'error')
                return redirect(url_for('company_detail', company_id=company.id))

            # Create recommendation
            recommendation = CompanyRecommendation(
                company_id=company.id,
                user_id=current_user.id,
                recommendation_text=recommendation_text,
                service_category=service_category if service_category else None,
                show_contact=show_contact,
                status='pending'
            )
            db.add(recommendation)
            db.commit()

            flash('Dziękujemy! Twoja rekomendacja została przesłana i oczekuje na moderację.', 'success')
            return redirect(url_for('company_detail', company_id=company.id))

        # Handle GET (show form)
        return render_template('company/recommend.html', company=company)
    finally:
        db.close()


@bp.route('/search')
@login_required
def search():
    """Search companies and people with advanced matching - requires login"""
    query = request.args.get('q', '')
    category_id = request.args.get('category', type=int)

    db = SessionLocal()
    try:
        # Use new SearchService with synonym expansion, NIP/REGON lookup, and fuzzy matching
        results = search_companies(db, query, category_id, limit=50)

        # Extract companies from SearchResult objects
        companies = [r.company for r in results]

        # Log search to analytics (SearchQuery table)
        if query:
            try:
                analytics_session_id = session.get('analytics_session_id')
                session_db_id = None
                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

                search_query = SearchQuery(
                    session_id=session_db_id,
                    user_id=current_user.id if current_user.is_authenticated else None,
                    query=query[:500],
                    query_normalized=query.lower().strip()[:500],
                    results_count=len(companies),
                    has_results=len(companies) > 0,
                    search_type='main',
                    filters_used={'category_id': category_id} if category_id else None
                )
                db.add(search_query)
                db.commit()
            except Exception as e:
                logger.error(f"Search logging error: {e}")
                db.rollback()

        # For debugging/analytics - log search stats
        if query:
            match_types = {}
            for r in results:
                match_types[r.match_type] = match_types.get(r.match_type, 0) + 1
            logger.info(f"Search '{query}': {len(companies)} results, types: {match_types}")

        # Search people by name (partial match)
        people_results = []
        if query and len(query) >= 2:
            q = f"%{query}%"
            people_results = db.query(Person).filter(
                or_(
                    Person.imiona.ilike(q),
                    Person.nazwisko.ilike(q),
                    func.concat(Person.imiona, ' ', Person.nazwisko).ilike(q)
                )
            ).limit(20).all()

            # For each person, get their company connections count
            for person in people_results:
                person.company_count = len(set(
                    r.company_id for r in person.company_roles
                    if r.company and r.company.status == 'active'
                ))

            logger.info(f"Search '{query}': {len(people_results)} people found")

        return render_template(
            'search_results.html',
            companies=companies,
            people=people_results,
            query=query,
            category_id=category_id,
            result_count=len(companies)
        )
    finally:
        db.close()


@bp.route('/aktualnosci')
@login_required
def events():
    """Company events and news - latest updates from member companies"""
    event_type_filter = request.args.get('type', '')
    company_id = request.args.get('company', type=int)
    page = request.args.get('page', 1, type=int)
    per_page = 20

    db = SessionLocal()
    try:
        # Build query
        query = db.query(CompanyEvent).join(Company)

        # Apply filters
        if event_type_filter:
            query = query.filter(CompanyEvent.event_type == event_type_filter)
        if company_id:
            query = query.filter(CompanyEvent.company_id == company_id)

        # Order by date (newest first)
        query = query.order_by(
            CompanyEvent.event_date.desc(),
            CompanyEvent.created_at.desc()
        )

        # Pagination
        total_events = query.count()
        events = query.limit(per_page).offset((page - 1) * per_page).all()

        # Get companies with events for filter dropdown
        companies_with_events = db.query(Company).join(CompanyEvent).distinct().order_by(Company.name).all()

        # Event type statistics
        event_types = db.query(
            CompanyEvent.event_type,
            func.count(CompanyEvent.id)
        ).group_by(CompanyEvent.event_type).all()

        return render_template(
            'events.html',
            events=events,
            companies_with_events=companies_with_events,
            event_types=event_types,
            event_type_filter=event_type_filter,
            company_id=company_id,
            page=page,
            per_page=per_page,
            total_events=total_events,
            total_pages=(total_events + per_page - 1) // per_page
        )
    finally:
        db.close()


@bp.route('/nowi-czlonkowie')
@login_required
def new_members():
    """Lista nowych firm członkowskich"""
    days = request.args.get('days', 90, type=int)

    db = SessionLocal()
    try:
        cutoff_date = datetime.now() - timedelta(days=days)

        new_companies = db.query(Company).filter(
            Company.status == 'active',
            Company.created_at >= cutoff_date
        ).order_by(Company.created_at.desc()).all()

        return render_template('new_members.html',
            companies=new_companies,
            days=days,
            total=len(new_companies)
        )
    finally:
        db.close()


@bp.route('/mapa-polaczen')
def connections_map():
    """Company-person connections visualization page"""
    return render_template('connections_map.html')


@bp.route('/dashboard')
@login_required
def dashboard():
    """User dashboard"""
    db = SessionLocal()
    try:
        # Get user's conversations
        conversations = db.query(AIChatConversation).filter_by(
            user_id=current_user.id
        ).order_by(AIChatConversation.updated_at.desc()).limit(10).all()

        # Stats
        total_conversations = db.query(AIChatConversation).filter_by(user_id=current_user.id).count()
        total_messages = db.query(AIChatMessage).join(AIChatConversation).filter(
            AIChatConversation.user_id == current_user.id
        ).count()

        # New stats
        unread_notifications = db.query(UserNotification).filter(
            UserNotification.user_id == current_user.id,
            UserNotification.is_read == False
        ).count()

        upcoming_events_count = db.query(NordaEvent).filter(
            NordaEvent.event_date >= date.today()
        ).count()

        user_forum_topics_count = db.query(ForumTopic).filter(
            ForumTopic.author_id == current_user.id,
            ForumTopic.is_deleted == False
        ).count()

        # Check for membership application status
        has_pending_application = False
        has_draft_application = False
        pending_application = None

        try:
            from database import MembershipApplication
            pending_application = db.query(MembershipApplication).filter(
                MembershipApplication.user_id == current_user.id,
                MembershipApplication.status.in_(['submitted', 'under_review', 'changes_requested'])
            ).first()
            has_pending_application = pending_application is not None

            if not has_pending_application:
                draft = db.query(MembershipApplication).filter(
                    MembershipApplication.user_id == current_user.id,
                    MembershipApplication.status == 'draft'
                ).first()
                has_draft_application = draft is not None
        except Exception:
            pass  # MembershipApplication table may not exist yet

        # Load user's company associations (multi-company support)
        from sqlalchemy.orm import joinedload
        user_companies = db.query(UserCompany).options(
            joinedload(UserCompany.company)
        ).filter_by(
            user_id=current_user.id
        ).order_by(UserCompany.is_primary.desc(), UserCompany.created_at.asc()).all()
        # Force-load company names before session closes
        for uc in user_companies:
            _ = uc.company.name if uc.company else None

        # Managers map for companies where user is EMPLOYEE (for edit permission modal)
        company_managers_map = {}
        employee_company_ids = [uc.company_id for uc in user_companies if uc.role == 'EMPLOYEE']
        if employee_company_ids:
            managers = db.query(User, UserCompany.company_id).join(UserCompany).filter(
                UserCompany.company_id.in_(employee_company_ids),
                UserCompany.role == 'MANAGER'
            ).all()
            for mgr, cid in managers:
                company_managers_map.setdefault(cid, []).append({'name': mgr.name or 'Brak imienia', 'email': mgr.email or ''})

        # Onboarding progress widget
        from utils.onboarding import compute_onboarding_steps
        onboarding = compute_onboarding_steps(current_user, user_companies, current_app.root_path)

        # Widget 1: Upcoming events (3 nearest future events)
        upcoming_events = db.query(NordaEvent).filter(
            NordaEvent.event_date >= date.today()
        ).order_by(NordaEvent.event_date.asc()).limit(3).all()

        # Batch RSVP lookup for current user
        user_event_ids = set()
        if upcoming_events:
            event_ids = [e.id for e in upcoming_events]
            rsvps = db.query(EventAttendee.event_id).filter(
                EventAttendee.event_id.in_(event_ids),
                EventAttendee.user_id == current_user.id,
                EventAttendee.status == 'confirmed'
            ).all()
            user_event_ids = {r[0] for r in rsvps}

        # Widget 2: Recent announcements (3 latest published, pinned first, not expired)
        recent_announcements = db.query(Announcement).filter(
            Announcement.status == 'published',
            or_(Announcement.expires_at == None, Announcement.expires_at > datetime.now())
        ).order_by(Announcement.is_pinned.desc(), Announcement.published_at.desc()).limit(3).all()

        # Widget 3: Recent forum topics (5 latest active)
        recent_forum_topics = db.query(ForumTopic).filter(
            ForumTopic.is_deleted == False
        ).order_by(ForumTopic.updated_at.desc()).limit(5).all()

        # Widget 4: Recent classifieds (3 active, not test, not expired)
        recent_classifieds = db.query(Classified).filter(
            Classified.is_active == True,
            Classified.is_test == False,
            or_(Classified.expires_at == None, Classified.expires_at > datetime.now())
        ).order_by(Classified.created_at.desc()).limit(3).all()

        # Widget 5: New companies (3 newest)
        new_companies = db.query(Company).order_by(
            Company.created_at.desc()
        ).limit(3).all()

        return render_template(
            'dashboard.html',
            conversations=conversations,
            total_conversations=total_conversations,
            total_messages=total_messages,
            has_pending_application=has_pending_application,
            has_draft_application=has_draft_application,
            pending_application=pending_application,
            user_companies=user_companies,
            unread_notifications=unread_notifications,
            upcoming_events_count=upcoming_events_count,
            user_forum_topics_count=user_forum_topics_count,
            upcoming_events=upcoming_events,
            user_event_ids=user_event_ids,
            recent_announcements=recent_announcements,
            recent_forum_topics=recent_forum_topics,
            recent_classifieds=recent_classifieds,
            new_companies=new_companies,
            company_managers_map=company_managers_map,
            onboarding=onboarding
        )
    finally:
        db.close()


@bp.route('/release-notes')
def release_notes():
    """Historia zmian platformy."""
    releases = [
        {
            'version': 'v1.32.0',
            'date': '14 lutego 2026',
            'badges': ['improve', 'fix'],
            'improve': [
                '<strong>Powiadomienia na telefonie</strong> - dropdown wyświetla się jako panel od dołu ekranu zamiast być obcinanym',
                '<strong>Dodawanie zdjęć na forum z telefonu</strong> - przycisk "Dodaj zdjęcie z galerii" zamiast "Ctrl+V" na urządzeniach mobilnych',
            ],
            'fix': [
                '<strong>Usuwanie odpowiedzi na forum</strong> - naprawiono błąd uniemożliwiający usunięcie odpowiedzi, które były już przeczytane przez innych',
                '<strong>Wysyłanie załączników z telefonu</strong> - naprawiono problem z nieprzesyłaniem zdjęć wybranych z galerii na urządzeniach mobilnych',
                '<strong>Pulpit użytkownika</strong> - naprawiono błąd 500 przy wyświetlaniu dashboardu',
            ],
        },
        {
            'version': 'v1.31.0',
            'date': '13 lutego 2026',
            'badges': ['new', 'improve', 'fix'],
            'new': [
                '<strong>Widget postępu wdrożenia na pulpicie</strong> - widoczny stan konfiguracji firmy po zalogowaniu',
                '<strong>Zdjęcia w wydarzeniach</strong> - wydarzenia mogą zawierać zdjęcia w opisie',
                '<strong>Nowa firma: UNIMOT Energia i Gaz</strong> - logotyp nowej firmy członkowskiej',
                '<strong>Weryfikacja certyfikatu SSL w audycie SEO</strong> - sprawdzanie ważności certyfikatu strony',
            ],
            'improve': [
                '<strong>Czytelniejsze opisy wydarzeń</strong> - poprawione formatowanie tekstu i wyświetlanie obrazów w kalendarzu',
                '<strong>Walidacja NIP przy rejestracji</strong> - sprawdzanie sumy kontrolnej numeru NIP',
            ],
            'fix': [
                '<strong>Audyt SEO</strong> - naprawiono ładowanie kluczy API przy uruchamianiu audytu',
            ],
        },
        {
            'version': 'v1.30.0',
            'date': '12 lutego 2026',
            'badges': ['new', 'improve', 'fix'],
            'new': [
                '<strong>3 najbliższe wydarzenia na stronie głównej</strong> - szybki podgląd kalendarza bez wchodzenia w szczegóły',
                '<strong>Nowe firmy: Alter Energy, Fiume Studio</strong> - logotypy nowych firm członkowskich',
                '<strong>Marki zależne dziedziczą NIP</strong> - marki podrzędne automatycznie wyświetlają NIP firmy macierzystej',
            ],
            'improve': [
                '<strong>Lepsza detekcja profili Facebook w audycie</strong> - wykluczenie pixeli śledzących i parametrów Meta z wyników',
            ],
            'fix': [
                '<strong>Synchronizacja użytkownik-firma</strong> - naprawiono powiązania w tabeli user_companies i nazwy pól kalendarza',
            ],
        },
        {
            'version': 'v1.29.0',
            'date': '9 lutego 2026',
            'badges': ['new', 'improve', 'fix'],
            'new': [
                '<strong>Analiza planu rozwoju Izby przez AI</strong> - sztuczna inteligencja ocenia postęp realizacji strategii i wskazuje braki',
                '<strong>Panel wiedzy ZOPK na stronie głównej</strong> - wybrane wpisy bazy wiedzy widoczne bez wchodzenia w sekcję ZOPK',
                '<strong>Przyciski zarządzania na stronach ZOPK</strong> - administratorzy mogą edytować treści bezpośrednio ze strony publicznej',
                '<strong>Baza wiedzy ZOPK w czasie rzeczywistym</strong> - postęp wyszukiwania i ekstrakcji widoczny na żywo w przeglądarce',
                '<strong>4-etapowy panel zarządzania bazą wiedzy ZOPK</strong> - workflow od wyszukiwania przez scraping po ekstrakcję',
                '<strong>Rozszerzenie Google API do ~100%</strong> - GBP Performance i Google Search Console w pełni zintegrowane',
                '<strong>Kontakt z przełożonym dla pracowników</strong> - pracownik widzi dane kontaktowe osoby zarządzającej firmą',
                '<strong>Pełna matryca uprawnień w panelu dostępu</strong> - widoczne wszystkie uprawnienia dla każdej roli',
                '<strong>Bezpośrednie ustawianie hasła przez admina</strong> - bez konieczności wysyłania linku resetującego',
                '<strong>Kolumna ostatniego logowania</strong> w liście użytkowników',
            ],
            'improve': [
                '<strong>Automatyczne odświeżanie tokenów OAuth</strong> - strona integracji nie wymaga ponownego logowania po wygaśnięciu tokenu',
                '<strong>Stylizowane okno potwierdzenia rozłączenia OAuth</strong> - zamiast standardowego okna przeglądarki',
                '<strong>Wydzielone przyciski audytu na profilu firmy</strong> - przeniesione do osobnego wiersza dla lepszej czytelności',
                '<strong>Link "Mój pulpit" na profilu firmy</strong> - szybki powrót do panelu użytkownika',
                '<strong>Dostęp do audytów ograniczony do wyznaczonego użytkownika</strong>',
                '<strong>Menu uproszczone dla zwykłych użytkowników</strong> - ukrycie opcji widocznych tylko dla właściciela platformy',
            ],
            'fix': [
                '<strong>Polskie znaki w audytach</strong> - naprawiono wyświetlanie polskich znaków diakrytycznych w szablonach audytu',
                '<strong>Baza wiedzy ZOPK</strong> - naprawiono model embeddingów, scraper treści i polskie komunikaty błędów',
                '<strong>Pulpit użytkownika</strong> - naprawiono błąd wylogowywania przy wyświetlaniu dashboardu z powiązanymi firmami',
            ],
        },
        {
            'version': 'v1.28.0',
            'date': '8 lutego 2026',
            'badges': ['new', 'improve', 'fix'],
            'new': [
                '<strong>Audyt SEO: dane z Google Search Console</strong> - kliknięcia, wyświetlenia i średnia pozycja z Google',
                '<strong>Audyt SEO: dane CrUX</strong> - rzeczywiste pomiary szybkości od użytkowników Chrome',
                '<strong>Audyt SEO: nagłówki bezpieczeństwa</strong> - sprawdzanie konfiguracji HTTPS i nagłówków ochronnych',
                '<strong>Audyt SEO: formaty obrazów</strong> - analiza wykorzystania nowoczesnych formatów WebP/AVIF',
                '<strong>Audyt SEO: pasek postępu i podsumowanie wyników</strong> - czytelna wizualizacja stanu strony',
                '<strong>Audyt SEO: instrukcje konfiguracji Search Console</strong> - krok po kroku dla firm bez połączonego konta',
                '<strong>Audyt GBP: migracja na Places API (New)</strong> - nowsze i dokładniejsze dane o firmach w Google Maps',
                '<strong>Audyt GBP: link do recenzji, wskazówki dojazdu, status otwarcia</strong>',
                '<strong>Audyt AI: analiza sentymentu recenzji i benchmarki branżowe</strong>',
                '<strong>Integracje OAuth</strong> - framework do łączenia kont Google i Meta z platformą',
            ],
            'improve': [
                '<strong>Migracja FID na INP</strong> - nowy standard Google do pomiaru interaktywności strony',
                '<strong>Rozszerzone wyświetlanie danych GBP i Social Media</strong> w panelach audytu',
            ],
            'fix': [
                '<strong>Audyt GBP</strong> - naprawiono fałszywe wykrywanie zdjęć i logo',
                '<strong>Audyt SEO</strong> - naprawiono zamianę klucza fid_ms na inp_ms w trasie',
                '<strong>Search Console</strong> - obsługa formatu domain property (sc-domain:)',
            ],
        },
        {
            'version': 'v1.27.0',
            'date': '6 lutego 2026',
            'badges': ['security', 'new', 'improve', 'fix'],
            'security': [
                '<strong>Przegląd bezpieczeństwa platformy</strong> - naprawiono 8 wykrytych luk (1 krytyczna, 7 średnich)',
                '<strong>Ochrona wyszukiwarki i bazy wiedzy ZOPK</strong> - zabezpieczenie przed atakami przez złośliwe zapytania',
                '<strong>Bezpieczne zapisywanie treści</strong> - oczyszczanie HTML w ogłoszeniach, wydarzeniach i protokołach Rady',
                '<strong>Ochrona kluczy dostępowych</strong> - klucze API nie są już widoczne w logach systemowych',
                '<strong>Zabezpieczenie formularzy</strong> - dodanie ochrony przed nieautoryzowanym wysyłaniem w chacie i 3 formularzach',
            ],
            'new': [
                # Forum
                '<strong>Powiadomienia email z forum</strong> - otrzymujesz email gdy ktoś odpowie w temacie, w którym uczestniczysz',
                '<strong>Automatyczna subskrypcja tematów</strong> - po dodaniu odpowiedzi automatycznie śledzisz dalszą dyskusję',
                '<strong>Rezygnacja z powiadomień</strong> - link w każdym emailu pozwala wyłączyć powiadomienia dla danego tematu',
                # NordaGPT
                '<strong>NordaGPT zna Izbę NORDA</strong> - chatbot odpowiada na pytania o misję, zarząd (16 osób), Akademię NORDA i Chwilę dla Biznesu',
                '<strong>Strategia 2026-2031 w NordaGPT</strong> - chatbot zna 3 kierunki rozwoju Izby, cel 30-lecia i kontekst regionalny Kaszub',
                '<strong>Projekty członkowskie w NordaGPT</strong> - chatbot zna projekty Energo Velo i Żarnowiecki Ring',
                # Dashboard
                '<strong>Sekcja "Co nowego w Izbie?" na pulpicie</strong> - po zalogowaniu widzisz: wydarzenia, ogłoszenia, tematy forum, oferty B2B i nowe firmy',
                '<strong>Aktualne dane na pulpicie</strong> - liczba nieprzeczytanych powiadomień i nadchodzących wydarzeń zamiast pustych statystyk',
                # Profil firmy - edycja
                '<strong>Edycja profilu firmy przez właściciela</strong> - właściciel może sam edytować opisy, usługi, kontakty i social media bez pomocy administratora',
                '<strong>Podział uprawnień w edycji</strong> - dane formalne (NIP, KRS, nazwa) zmienia tylko administrator; dane marketingowe mogą edytować uprawnieni pracownicy',
                '<strong>Więcej informacji na profilu firmy</strong> - wyświetlanie usług, technologii, zasięgu działania, języków, historii i wartości firmy',
                # Rejestry urzędowe
                '<strong>Pobieranie danych z rejestrów urzędowych</strong> - administrator jednym kliknięciem pobiera dane firmy z KRS, Białej Listy VAT lub CEIDG',
                '<strong>Automatyczny dobór rejestru</strong> - system sam wybiera właściwy rejestr: KRS dla spółek, CEIDG dla jednoosobowych działalności',
                '<strong>Import zarządu i branż z KRS</strong> - pobierane są osoby w zarządzie i kody PKD (branże działalności)',
                # Social audit
                '<strong>Ostrzeżenie o adresie Facebook</strong> - audyt wykrywa firmy używające numerycznego ID zamiast własnej nazwy na Facebooku',
                '<strong>Zalecenia dla Facebooka</strong> - podpowiedź aby przekształcić profil osobisty w stronę firmową z czytelnym adresem',
                '<strong>Zalecenia w panelu admina</strong> - administrator widzi kolorowe zalecenia dla każdej firmy (np. brak Facebooka, brak Instagrama)',
                # Loga firm
                '<strong>Nowe firmy: Termo i Studio N°33</strong> - dodano logotypy nowych firm członkowskich',
            ],
            'improve': [
                '<strong>Nowy wygląd formularza edycji firmy</strong> - czytelniejszy układ z zakładkami, ikonami i licznikiem znaków',
                '<strong>Czytelniejsze podpowiedzi</strong> - po najechaniu na awatar na forum i w ogłoszeniach B2B widać czytelną etykietkę z imieniem',
                '<strong>Lepsza detekcja adresów Facebook</strong> - poprawne rozpoznawanie profili z numerycznym ID i nietypowych adresów',
                '<strong>Zalecenia social media nawet gdy wszystko jest OK</strong> - wyświetlanie podpowiedzi np. o zmianie adresu Facebook, nawet gdy firma ma wszystkie platformy',
                '<strong>Kolumna zaleceń w panelu Social Audit</strong> - kolorowe etykiety (czerwone, pomarańczowe, szare, zielone) dla szybkiej oceny',
                '<strong>Pulpit z dwukolumnowym układem</strong> - widgety z aktywnością Izby czytelnie rozmieszczone na ekranie',
            ],
            'fix': [
                '<strong>Zatwierdzanie propozycji AI</strong> - naprawiono błąd przy klikaniu "Akceptuj i dodaj do profilu"',
                '<strong>Linki do Facebooka</strong> - naprawiono błędne linki dla firm z numerycznym ID na Facebooku',
                '<strong>Audyt SEO ponownie dostępny</strong> - przywrócono działanie usługi audytu SEO po reorganizacji kodu',
                '<strong>Audyt Google Business Profile ponownie dostępny</strong> - przywrócono działanie usługi audytu GBP',
                '<strong>Wyświetlanie profilu firmy</strong> - naprawiono błąd uniemożliwiający otwarcie niektórych profili firm',
                '<strong>Podpowiedzi na awatarach</strong> - poprawiona czytelność etykietek z imionami na forum i w ogłoszeniach',
            ],
        },
        {
            'version': 'v1.26.0',
            'date': '5 lutego 2026',
            'badges': ['security', 'improve'],
            'security': [
                '<strong>System uprawnień: 154 trasy zabezpieczone</strong> - każda strona administracyjna wymaga teraz odpowiedniego poziomu dostępu',
                '<strong>6-poziomowa hierarchia ról</strong> - od zwykłego użytkownika przez członka, pracownika, kierownika po administratora',
                '<strong>Menu dostosowane do roli</strong> - kierownik biura widzi tylko te opcje, do których ma uprawnienia',
                '<strong>Wybór roli przy tworzeniu użytkownika</strong> - zamiast prostego "tak/nie" administrator wybiera konkretny poziom dostępu',
            ],
            'improve': [
                '<strong>23 testy automatyczne dla systemu ról</strong> - weryfikacja poprawności uprawnień na każdym poziomie',
                '<strong>Trwałe usuwanie firm</strong> - zarchiwizowane firmy mogą być trwale usunięte przez administratora (nieodwracalne)',
            ],
        },
        {
            'version': 'v1.25.0',
            'date': '4 lutego 2026',
            'badges': ['new', 'improve', 'fix'],
            'new': [
                '<strong>Strefa RADA</strong> - zamknięta sekcja dla członków Rady Izby z listą posiedzeń i członków',
                '<strong>Zarządzanie posiedzeniami Rady</strong> - program, lista obecności i protokół w jednym miejscu',
                '<strong>Edytor protokołu</strong> - zapisywanie ustaleń, decyzji i zadań z osobą odpowiedzialną i terminem',
                '<strong>Pobieranie PDF</strong> - program posiedzenia i protokół do pobrania jako dokument PDF',
                '<strong>Lista obecności z kworum</strong> - automatyczne liczenie obecnych i sprawdzanie kworum',
                '<strong>Publikowanie programu i protokołu</strong> - osobne publikowanie każdego dokumentu',
                '<strong>Korzyści dla Członków</strong> - oferty partnerskie (WisprFlow AI) dostępne dla członków Izby',
                '<strong>Strona korzyści</strong> - przegląd ofert partnerskich z linkami do wersji demonstracyjnych',
                '<strong>Ulepszona rejestracja</strong> - po weryfikacji email automatyczne zalogowanie i przekierowanie',
                '<strong>Wydarzenia Rady</strong> widoczne tylko dla członków Izby',
                '<strong>Status wniosku członkowskiego</strong> - po złożeniu wniosku widać jego aktualny stan',
                '<strong>Powiadomienie dla administratora</strong> o nowym wniosku członkowskim',
                '<strong>Szczegóły profilu firmy</strong> widoczne tylko dla członków Izby',
            ],
            'improve': [
                '<strong>Statusy posiedzeń jako klikalne linki</strong> do programu i protokołu',
                '<strong>Czytelne wyświetlanie przebiegu posiedzenia</strong> z decyzjami i zadaniami',
                '<strong>Środowisko testowe</strong> oznaczone wizualnie, aby nie pomylić z produkcją',
                'Zablokowane wersje bibliotek dla stabilności platformy',
                'Aktualizacja bibliotek systemowych',
                '<strong>Strefa RADA uproszczona</strong> - skupiona na posiedzeniach',
                '<strong>Korzyści</strong> - dane o prowizjach widoczne tylko dla właściciela oferty',
                '<strong>Trwałe usuwanie firm</strong> - administrator może nieodwracalnie usunąć zarchiwizowane firmy',
            ],
            'fix': [
                '<strong>Naprawiono zabezpieczenie formularzy</strong> publikacji programu i protokołu',
                'Naprawiono wyświetlanie posiedzeń bez programu lub punktów obrad',
                'Naprawiono przycisk potwierdzenia udziału w wydarzeniach',
                'Naprawiono link do składania wniosku członkowskiego',
                'Obsługa sytuacji gdy generowanie PDF jest tymczasowo niedostępne',
                '<strong>Naprawiono błąd przy usuwaniu użytkowników</strong> powiązanych z innymi danymi',
            ],
        },
        {
            'version': 'v1.24.0',
            'date': '2 lutego 2026',
            'badges': ['new', 'improve'],
            'new': [
                '<strong>Środowisko testowe</strong> - osobny serwer do sprawdzania zmian przed wdrożeniem',
                '<strong>Automatyczne testy</strong> - każda zmiana w kodzie jest automatycznie sprawdzana',
                '<strong>Testy logowania i sesji</strong> użytkowników',
                '<strong>Testy bezpieczeństwa</strong> - weryfikacja ochrony przed najczęstszymi atakami',
                '<strong>Testy w przeglądarce</strong> - automatyczne sprawdzanie działania strony',
                '<strong>Automatyczna weryfikacja</strong> po każdym wdrożeniu na produkcję',
            ],
            'improve': [
                'Automatyczna kontrola jakości kodu przed zapisaniem zmian',
                'Narzędzia do utrzymania spójności kodu',
                'Wskaźnik statusu testów widoczny na stronie projektu',
            ],
        },
        {
            'version': 'v1.23.0',
            'date': '1 lutego 2026',
            'badges': ['security', 'new', 'improve', 'fix'],
            'security': [
                '<strong>6 poziomów dostępu</strong> - od gościa przez członka, pracownika, kierownika po administratora',
                '<strong>NordaGPT dostępny tylko dla członków Izby</strong>',
                '<strong>Wiadomości prywatne</strong> tylko dla członków Izby',
                '<strong>Tablica ogłoszeń B2B</strong> tylko dla członków Izby',
                '<strong>Dane kontaktowe firm</strong> widoczne tylko dla członków Izby',
            ],
            'new': [
                '<strong>Składanie wniosków o członkostwo</strong> - formularz, weryfikacja danych, zatwierdzanie przez admina',
                '<strong>Automatyczne wyszukiwanie danych firmy</strong> po numerze NIP',
                '<strong>Porównanie danych</strong> podanych przez użytkownika z danymi z rejestrów urzędowych',
                '<strong>Zatwierdzanie danych z rejestrów</strong> - użytkownik może zatwierdzić lub odrzucić pobrane dane',
                '<strong>Historia procesu</strong> - oś czasu wszystkich kroków od złożenia do zatwierdzenia wniosku',
                '<strong>Powiadomienie dla administratora</strong> o decyzji użytkownika ws. danych z rejestrów',
                '<strong>Sekcja "Dane z rejestrów urzędowych"</strong> na profilu firmy (KRS lub CEIDG)',
                '<strong>Pełne dane z KRS</strong> - kapitał zakładowy, sposób reprezentacji, wspólnicy',
                '<strong>Automatyczny dobór rejestru</strong> - KRS dla spółek, CEIDG dla jednoosobowych firm',
                '<strong>Automatyczne pobieranie danych z KRS</strong> przy zatwierdzaniu wniosku',
                'Strona promocyjna NordaGPT dla osób niebędących członkami',
                'Osobne uprawnienia dla kierownika biura Izby',
                'Funkcje portalu wymagające członkostwa w Izbie',
                'Panel przypisywania ról użytkownikom',
                '<strong>Automatyczna aktualizacja opisów firm</strong> - AI analizuje strony internetowe członków',
            ],
            'improve': [
                '<strong>Czytelniejszy profil firmy</strong> - usunięcie powtarzających się informacji',
                'Dane kontaktowe zebrane w jednym miejscu na profilu',
                'Podział funkcji administracyjnych według poziomu uprawnień',
                'Moderacja forum dostępna dla uprawnionych osób',
                'Menu dostosowane do uprawnień użytkownika',
                'Usunięcie automatycznie generowanych sekcji z profilu firmy',
                'Tymczasowe ukrycie sekcji rekomendacji',
            ],
            'fix': [
                '<strong>Naprawiono zapisywanie adresu</strong> przy tworzeniu nowej firmy',
                'Naprawiono linki do profili firm',
                'Naprawiono zabezpieczenie formularzy członkostwa',
                'Naprawiono błąd przy składaniu wniosku członkowskiego',
                'Naprawiono zapisywanie historii procesu członkostwa',
                'Naprawiono okno potwierdzenia, które traciło dane po zamknięciu',
            ],
        },
        {
            'version': 'v1.22.0',
            'date': '31 stycznia 2026',
            'badges': ['new', 'improve', 'fix'],
            'new': [
                '<strong>Tablica B2B: Przycisk "Jestem zainteresowany"</strong> - wyrażenie zainteresowania ofertą',
                '<strong>Tablica B2B: Publiczne pytania i odpowiedzi</strong> pod ogłoszeniami',
                '<strong>Tablica B2B: Wysyłanie wiadomości</strong> bezpośrednio z ogłoszenia',
                'Tablica B2B: Autor widzi kto jest zainteresowany jego ofertą',
                'Tablica B2B: Oznaczenie wiadomości powiązanych z ogłoszeniem',
                '<strong>Forum: Informacja kto przeczytał</strong> każdą odpowiedź',
                '<strong>Tablica B2B: Informacja kto widział</strong> ogłoszenie',
                '<strong>Panel admina: Zarządzanie firmami</strong> - lista, edycja, statystyki',
                '<strong>Panel admina: Zarządzanie osobami</strong> - dane z KRS i powiązania z firmami',
                '<strong>Panel admina: Przegląd stanu platformy</strong> - certyfikaty, bezpieczeństwo',
                '<strong>Rejestr logowań</strong> - kto i kiedy się logował do platformy',
                '<strong>Forum: Reakcje emoji</strong> na wpisy i odpowiedzi',
                '<strong>Forum: Śledzenie tematów</strong> z powiadomieniami o nowych odpowiedziach',
                '<strong>Forum: Edycja własnych wpisów</strong> (do 24 godzin)',
                '<strong>Forum: Zgłaszanie nieodpowiednich treści</strong>',
                '<strong>Forum: Oznaczanie najlepszej odpowiedzi</strong> jako rozwiązanie',
                'Forum: Statystyki aktywności użytkownika',
                'Forum: Formatowanie tekstu (pogrubienie, listy, linki)',
                'Forum: Oznaczanie @użytkowników z powiadomieniami',
                '<strong>Panel admina: Analityka forum</strong> - wykresy aktywności i ranking użytkowników',
                'Panel admina: Eksport aktywności forum do arkusza',
                'Panel admina: Zbiorcze zarządzanie tematami forum',
                'Panel admina: Przenoszenie tematów między kategoriami',
                'Panel admina: Łączenie powiązanych tematów forum',
                'Panel admina: Wyszukiwarka z dostępem do usuniętych treści',
                'Panel admina: Historia aktywności użytkowników na forum',
                'Panel admina: Przywracanie usuniętych wpisów na forum',
                'Menu admina: Szybki dostęp do Forum, Ogłoszeń i Analityki AI',
            ],
            'improve': [
                '<strong>Reorganizacja kodu platformy</strong> dla łatwiejszego rozwoju',
                'Forum: Oznaczenie "(Ty)" przy własnym awatarze',
                'Czytelniejszy układ informacji o certyfikatach bezpieczeństwa',
                'Porządki w kodzie platformy',
            ],
            'fix': [
                '<strong>NordaGPT: Naprawiono pole wpisywania wiadomości</strong>, które było ucięte',
            ],
        },
        {
            'version': 'v1.21.0',
            'date': '30 stycznia 2026',
            'badges': ['new', 'improve', 'fix'],
            'new': [
                '<strong>Moje konto</strong> - edycja danych osobowych, ustawienia prywatności i bezpieczeństwa',
                '<strong>Moderacja forum</strong> - administrator może usuwać, przypinać i blokować wpisy',
                '<strong>Moderacja ogłoszeń B2B</strong> - administrator może usuwać i dezaktywować ogłoszenia',
                'Podgląd hasła - ikonka oka pozwala zobaczyć wpisywane hasło',
                'Ładniejsze okna potwierdzenia na forum',
                'Ładniejsze okna potwierdzenia w ogłoszeniach B2B',
                'Wątek na forum do zgłaszania pomysłów i uwag',
            ],
            'improve': [
                'Poprawna nazwa platformy na stronie rejestracji',
                'Przyjazna strona informacyjna podczas aktualizacji platformy',
            ],
            'fix': [
                '<strong>Reset hasła</strong> nie wymaga już ponownej weryfikacji adresu email',
                'Usunięto tymczasowe wideo z sekcji edukacyjnej',
            ],
        },
        {
            'version': 'v1.20.0',
            'date': '29 stycznia 2026',
            'badges': ['new', 'improve', 'fix'],
            'new': [
                '<strong>NordaGPT: Nowy silnik AI</strong> - Google Gemini 3 Flash z lepszym rozumieniem pytań',
                '<strong>NordaGPT: Dwa tryby</strong> - podstawowy (bezpłatny) i zaawansowany (dokładniejszy)',
                'NordaGPT: 7x lepsze rozumowanie i dokładniejsze odpowiedzi',
                'NordaGPT: Informacja o szacowanym koszcie użytkowania',
                '<strong>Aplikacja mobilna</strong> - portal można zainstalować na telefonie jak aplikację (iOS/Android)',
                'Aktualności: Ogłoszenie może należeć do kilku kategorii jednocześnie',
                'Aktualności: Nowe kategorie - Wewnętrzne, Zewnętrzne, Wydarzenie, Okazja biznesowa, Partnerstwo',
                'Sekcja edukacyjna: Materiały wideo do obejrzenia na portalu',
                'Film powitalny "Wprowadzenie do Norda Biznes Partner"',
                '<strong>Administrator otrzymuje email</strong> o każdej nowej rejestracji',
            ],
            'improve': [
                'Nowa ikona NordaGPT na stronie głównej',
                'Porządki w stopce strony',
            ],
            'fix': [
                'Naprawiono błąd przy dodawaniu ogłoszeń B2B',
                'Naprawiono błąd przy dodawaniu wydarzeń do kalendarza',
                'Naprawiono nawigację w module kontaktów',
            ],
        },
        {
            'version': 'v1.19.0',
            'date': '28 stycznia 2026',
            'badges': ['new', 'improve', 'security'],
            'new': [
                '<strong>Ukrywanie telefonu i email</strong> - można wybrać w ustawieniach, co jest widoczne na profilu',
                '<strong>Blokowanie użytkowników</strong> - zablokowana osoba nie może wysyłać wiadomości',
                'Wybór preferowanego sposobu kontaktu (email, telefon, portal)',
                '<strong>Kategorie branżowe</strong> - 4 główne grupy z podkategoriami',
                'Oznaczenie firm z niekompletnym profilem do uzupełnienia',
                'Nowe podkategorie branżowe: Budownictwo, Produkcja, Usługi finansowe',
                '<strong>Nowa sekcja Edukacja</strong> w menu platformy',
                'Panel zbierania opinii i pomysłów od użytkowników',
                'Rozszerzony monitoring stanu platformy',
            ],
            'improve': [
                'Katalog: Wyraźne oznaczenie wybranej kategorii',
                'Kategorie posortowane od największej liczby firm',
            ],
            'security': [
                '<strong>Ochrona danych osobowych</strong> - chatbot automatycznie ukrywa numery PESEL, karty i IBAN',
                '<strong>Prywatność rozmów</strong> - każdy użytkownik widzi tylko swoje rozmowy z chatbotem',
                'Anonimowe statystyki rozmów z chatbotem w panelu admina',
            ],
        },
        {
            'version': 'v1.17.0',
            'date': '26 stycznia 2026',
            'badges': ['new'],
            'new': [
                '<strong>Sekcja Aktualności</strong> - wiadomości i ogłoszenia dla członków Izby',
                'Panel zarządzania aktualnościami dla administratora',
                'Kategorie aktualności, możliwość przypinania ważnych ogłoszeń',
                'Załączniki PDF i linki w aktualnościach',
                'Pierwsze ogłoszenia: Baza noclegowa ARP, Konkurs Tytani Przedsiębiorczości',
            ],
        },
        {
            'version': 'v1.16.0',
            'date': '14 stycznia 2026',
            'badges': ['new', 'improve', 'fix'],
            'new': [
                '<strong>Ochrona geograficzna</strong> - blokowanie dostępu z krajów wysokiego ryzyka',
                '<strong>Własna domena email</strong> - wiadomości wysyłane z adresu @nordabiznes.pl',
                '<strong>Raporty</strong> - staż członkostwa, obecność w social media, struktura branżowa',
                'Data przystąpienia do Izby na profilu firmy z informacją o stażu',
                'Pobieranie danych jednoosobowych firm z rejestru CEIDG',
                'Panel bezpieczeństwa z oceną poziomu ochrony platformy',
            ],
            'improve': [
                'Uzupełniono rok założenia dla 71 firm (64% katalogowanych)',
                'Uzupełniono daty przystąpienia do Izby dla 57 firm (od 1997 roku)',
            ],
            'fix': [
                'Naprawiono wyświetlanie polskich znaków w statystykach',
            ],
        },
        {
            'version': 'v1.15.0',
            'date': '13 stycznia 2026',
            'badges': ['new', 'improve', 'fix'],
            'new': [
                '<strong>NordaGPT zna więcej danych</strong> - rekomendacje, kalendarz, ogłoszenia B2B, forum i dane KRS',
                '<strong>NordaGPT: Klikalne linki</strong> i adresy email w odpowiedziach chatbota',
                '<strong>NordaGPT: Szybki dostęp</strong> do chatbota ze strony głównej',
                '<strong>Kalendarz: Widok miesięczny</strong> z szybkim potwierdzaniem udziału',
                'Najbliższe wydarzenie widoczne na stronie głównej z listą uczestników',
                '<strong>Wzbogacanie profili firm przez AI</strong> - automatyczne uzupełnianie informacji z internetu',
                '<strong>Sprawdzanie danych KRS</strong> z raportami postępu',
                '<strong>Panel analityki</strong> - statystyki odwiedzin i aktywności użytkowników',
                'Profil firmy: Pełna lista branż (kody PKD) i dane właściciela',
                'Zielone oznaczenie przy osobach zweryfikowanych w rejestrze KRS',
            ],
            'improve': [
                'Czytelniejsze formatowanie odpowiedzi NordaGPT',
                'Możliwość zwinięcia bannera NordaGPT na stronie głównej',
            ],
            'fix': [
                'Zwiększony limit logowań i audytów SEO',
            ],
        },
        {
            'version': 'v1.14.0',
            'date': '12 stycznia 2026',
            'badges': ['new', 'improve', 'fix'],
            'new': [
                '<strong>Audyt wizytówki Google</strong> - sprawdzanie kompletności profilu Google dla każdej firmy',
                'Poradnik "Jak działa wizytówka Google?" w sekcji audytu',
                'Wyniki audytów widoczne bezpośrednio na profilu firmy',
            ],
            'improve': [
                'Jednolita 5-stopniowa skala ocen we wszystkich audytach',
                'Wynik audytu social media jako procent zamiast liczby platform',
            ],
            'fix': [
                'Kategorie Google wyświetlane po polsku',
            ],
        },
        {
            'version': 'v1.13.0',
            'date': '11 stycznia 2026',
            'badges': ['new', 'improve'],
            'new': [
                '<strong>Mapa Powiązań</strong> - interaktywna wizualizacja powiązań między firmami i osobami',
                '<strong>Profile osób</strong> - dane z rejestrów urzędowych i portalu',
                '<strong>NordaGPT uczy się z opinii użytkowników</strong> i poprawia odpowiedzi',
                'Wyszukiwanie osób po częściowym imieniu lub nazwisku',
                'Logo firm widoczne w wynikach wyszukiwania',
                'Panel użycia AI - statystyki rozmów dla każdego użytkownika',
            ],
            'improve': [
                'Mapa Powiązań: pełnoekranowy widok z podpowiedziami po najechaniu',
                'Ładniejsze powiadomienia zamiast systemowych okien',
            ],
        },
        {
            'version': 'v1.11.0',
            'date': '10 stycznia 2026',
            'badges': ['new', 'improve', 'security'],
            'new': [
                '<strong>Forum: Wstawianie zdjęć</strong> - przeciągnij, wklej ze schowka, do 10 plików',
                '<strong>Forum: Kategorie wpisów</strong> - Propozycja, Błąd, Pytanie',
                '<strong>Kompletna dokumentacja techniczna</strong> platformy',
            ],
            'improve': [
                'Bezpieczne przesyłanie plików ze sprawdzaniem zawartości',
            ],
            'security': [
                '<strong>Usunięcie haseł z kodu źródłowego</strong>',
                'Zmiana hasła bazy danych na serwerze produkcyjnym',
            ],
        },
        {
            'version': 'v1.9.0',
            'date': '9 stycznia 2026',
            'badges': ['new', 'improve'],
            'new': [
                '<strong>Audyt wizytówek Google</strong> - przegląd profili Google Business wszystkich firm',
                '<strong>Audyt Social Media</strong> - sprawdzanie obecności firm w mediach społecznościowych',
                '<strong>Tworzenie użytkowników przez AI</strong> - wystarczy wkleić tekst lub zrzut ekranu',
            ],
            'improve': [
                'Nowy pasek administracyjny z pogrupowanymi funkcjami',
            ],
        },
        {
            'version': 'v1.8.0',
            'date': '8 stycznia 2026',
            'badges': ['new'],
            'new': [
                '<strong>Audyt IT</strong> - sprawdzanie infrastruktury informatycznej firm członkowskich',
                'Eksport wyników audytu IT do arkusza kalkulacyjnego',
            ],
        },
        {
            'version': 'v1.7.0',
            'date': '6 stycznia 2026',
            'badges': ['new'],
            'new': [
                '<strong>Audyt SEO</strong> - analiza widoczności stron internetowych firm w wyszukiwarkach',
                '<strong>Ocena szybkości stron</strong> przez Google PageSpeed',
            ],
        },
        {
            'version': 'v1.6.0',
            'date': '29 grudnia 2025',
            'badges': ['new'],
            'new': [
                '<strong>Wzmianki medialne</strong> - automatyczne wyszukiwanie artykułów o firmach członkowskich',
                'Panel moderacji wzmianek dla administratora',
                'Wyszukiwanie wzmianek przez wyszukiwarkę Brave',
            ],
        },
        {
            'version': 'v1.5.0',
            'date': '15 grudnia 2025',
            'badges': ['new', 'improve'],
            'new': [
                '<strong>Panel Social Media</strong> - zarządzanie profilami firm w mediach społecznościowych',
                'Sprawdzanie czy profile social media firm są aktywne',
            ],
            'improve': [
                'Sekcja Social Media na profilu firmy',
            ],
        },
        {
            'version': 'v1.4.0',
            'date': '1 grudnia 2025',
            'badges': ['new'],
            'new': [
                '<strong>Rekomendacje</strong> - firmy mogą polecać się nawzajem',
                '<strong>Panel składek członkowskich</strong>',
                '<strong>Kalendarz wydarzeń</strong> Izby',
            ],
        },
        {
            'version': 'v1.3.0',
            'date': '28 listopada 2025',
            'badges': ['new', 'improve'],
            'new': [
                '<strong>Chatbot NordaGPT</strong> - asystent AI znający wszystkie firmy członkowskie',
                '<strong>Wyszukiwarka firm</strong> - rozpoznaje synonimy i literówki',
            ],
            'improve': [
                'Szybsza i dokładniejsza wyszukiwarka',
            ],
        },
        {
            'version': 'v1.2.0',
            'date': '25 listopada 2025',
            'badges': ['new'],
            'new': [
                '<strong>System wiadomości prywatnych</strong> między użytkownikami',
                'Powiadomienia o nowych wiadomościach',
            ],
        },
        {
            'version': 'v1.1.0',
            'date': '24 listopada 2025',
            'badges': ['new', 'improve'],
            'new': [
                '<strong>Rejestracja i logowanie</strong> użytkowników',
                'Profile użytkowników powiązane z firmami',
            ],
            'improve': [
                'Strona dostosowana do telefonów i tabletów',
            ],
        },
        {
            'version': 'v1.0.0',
            'date': '23 listopada 2025',
            'badges': ['new'],
            'new': [
                '<strong>Oficjalny start platformy Norda Biznes Partner</strong>',
                '<strong>Katalog 111 firm członkowskich</strong>',
                'Wyszukiwarka firm po nazwie, branży i usługach',
                'Profile firm z pełnymi danymi kontaktowymi',
            ],
        },
    ]

    # Statystyki (używa globalnej stałej COMPANY_COUNT_MARKETING)
    db = SessionLocal()
    try:
        stats = {
            'companies': COMPANY_COUNT_MARKETING,
            'categories': db.query(Category).filter(Category.parent_id.isnot(None)).count(),
        }
    finally:
        db.close()

    return render_template('release_notes.html', releases=releases, stats=stats)
