"""
Veventa inside the panel.

Every list here is filtered by tenant and, where the user is scoped to one
company, by company too. That filtering happens once, in _book() and _scope(),
rather than in each view, because a filter repeated in twenty places is a
filter that will eventually be forgotten in one of them.
"""

from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.db.models import Q
from django.shortcuts import render, get_object_or_404

from apps.accounts.decorators import cms_required
from apps.tenants.resolve import resolve_tenant

from .models import Event, Company, CompanyAccess, CalendarNote
from . import scoring


def _companies(request, tenant):
    """Which companies this user may see.

    A System Administrator or Event Manager sees all of them. Anyone with
    explicit company access is held to it, which is how Event Specialist is
    scoped to their own company.
    """
    qs = Company.objects.filter(tenant=tenant)
    role = getattr(request.user, 'role', '')
    if role in ('system_admin', 'super_admin', 'event_manager'):
        return qs
    allowed = CompanyAccess.objects.filter(user=request.user).values_list(
        'company_id', flat=True)
    return qs.filter(pk__in=list(allowed))


def _events(request, tenant):
    """Events this user may reach, before any filtering they asked for.

    Operations sees only what they are assigned. That is an access rule, not a
    convenience, so it is applied here and never relaxed downstream.
    """
    qs = Event.objects.filter(tenant=tenant).select_related(
        'brand', 'venue', 'main_act', 'promoter')
    role = getattr(request.user, 'role', '')
    if role == 'operations':
        return qs.filter(assignments__user=request.user)
    companies = _companies(request, tenant)
    return qs.filter(Q(brand__company__in=companies) | Q(brand__isnull=True))


def _can_see_money(request):
    """Operations never sees a figure. The rule is applied by leaving the
    numbers out of the context, not by hiding them in the template."""
    return getattr(request.user, 'role', '') != 'operations'


@cms_required
def events_list(request):
    tenant = resolve_tenant(request)
    qs = _events(request, tenant)
    total = qs.count()

    q      = request.GET.get('q', '').strip()
    brand  = request.GET.get('brand', '').strip()
    year   = request.GET.get('year', '').strip()
    etype  = request.GET.get('type', '').strip()
    city   = request.GET.get('city', '').strip()
    mode   = request.GET.get('mode', '').strip()
    status = request.GET.get('status', '').strip()

    if q:
        qs = qs.filter(Q(name__icontains=q) | Q(city__icontains=q)
                       | Q(venue__name__icontains=q)
                       | Q(main_act__name__icontains=q))
    if brand:  qs = qs.filter(brand_id=brand)
    if year:   qs = qs.filter(date__year=year)
    if etype:  qs = qs.filter(event_type=etype)
    if city:   qs = qs.filter(city=city)
    if mode:   qs = qs.filter(mode=mode)
    if status: qs = qs.filter(status=status)

    page = Paginator(qs, 25).get_page(request.GET.get('page'))

    rows = []
    for e in page:
        row = {'obj': e, 'score': None, 'pl': None, 'sold': None, 'alloc': None}
        if _can_see_money(request):
            row['pl'] = scoring._pl(e)
            row['sold'] = scoring._sold(e)
            row['alloc'] = scoring._alloc(e)
        rows.append(row)

    qd = request.GET.copy(); qd.pop('page', None)
    return render(request, 'cms/veventa/events.html', {
        'rows': rows,
        'page_obj': page,
        'total': total,
        'show_money': _can_see_money(request),
        'companies': _companies(request, tenant),
        'filter_qs': qd.urlencode(),
        'q': q,
        'status_choices': Event.STATUS_CHOICES,
        'mode_choices': Event.MODE_CHOICES,
    })


@cms_required
def event_detail(request, pk):
    tenant = resolve_tenant(request)
    event = get_object_or_404(_events(request, tenant), pk=pk)
    tab = request.GET.get('tab', 'overview')
    money = _can_see_money(request)

    ctx = {'event': event, 'tab': tab, 'show_money': money,
           'tabs': [('overview', 'Overview'), ('box_office', 'Box Office'),
                    ('production', 'Production'), ('promotion', 'Promotion'),
                    ('finance', 'Finance'), ('milestones', 'Milestones'),
                    ('agenda', 'Agenda'), ('analysis', 'Analysis'),
                    ('documents', 'Documents'), ('gallery', 'Gallery')]}

    if money:
        book = Event.objects.filter(tenant=tenant).booked()
        ctx['forecast'] = scoring.forecast(event, book)
        ctx['revenue'] = scoring._revenue(event)
        ctx['cost'] = scoring._cost(event)
        ctx['pl'] = scoring._pl(event)
        ctx['sold'] = scoring._sold(event)
        ctx['alloc'] = scoring._alloc(event)
        ctx['factors'] = scoring.FACTORS

    if tab == 'box_office':
        ctx['tiers'] = event.tiers.all()
    elif tab == 'production':
        ctx['budget'] = event.budget.select_related('supplier')
    elif tab == 'promotion':
        ctx['promotion'] = event.promotion.all()
    elif tab == 'finance':
        ctx['tiers'] = event.tiers.all()
        ctx['income'] = event.income.select_related('partner')
    elif tab == 'milestones':
        ctx['plan'] = event.plan.all()
    elif tab == 'agenda':
        ctx['agenda'] = event.plan.all()
    elif tab == 'documents':
        ctx['documents'] = event.documents.all()
    elif tab == 'gallery':
        ctx['images'] = event.images.all()

    return render(request, 'cms/veventa/event_detail.html', ctx)


# ── The directories ─────────────────────────────────────────────────────────
# Artists, venues, partners, promoters, suppliers and contacts are the same
# screen with different columns, so they share one view rather than six near
# copies that drift apart.

from .models import Artist, Venue, Partner, Promoter, Supplier, Contact

DIRECTORIES = {
    'artists':   (Artist,   'Artists',   ['name', 'roles', 'country']),
    'venues':    (Venue,    'Venues',    ['name', 'city', 'capacity']),
    'partners':  (Partner,  'Partners',  ['name', 'categories', 'city']),
    'promoters': (Promoter, 'Promoters', ['name', 'categories', 'city']),
    'suppliers': (Supplier, 'Suppliers', ['name', 'category', 'city']),
    'contacts':  (Contact,  'Contacts',  ['name', 'kind', 'city']),
}


def _directory(request, key):
    tenant = resolve_tenant(request)
    model, title, cols = DIRECTORIES[key]
    qs = model.objects.filter(tenant=tenant)

    q = request.GET.get('q', '').strip()
    if q:
        qs = qs.filter(name__icontains=q)

    events = _events(request, tenant).booked()
    rows = []
    for obj in qs[:500]:
        rating = getattr(obj, 'rating_avg', None)
        # An unrated party has no score. The old app showed 80, which made a
        # stranger look as safe as somebody with ten good dates behind them.
        rows.append({'obj': obj, 'score': round(rating * 10) if rating else None})

    page = Paginator(rows, 25).get_page(request.GET.get('page'))
    return render(request, 'cms/veventa/directory.html', {
        'rows': page, 'page_obj': page, 'title': title,
        'total': qs.count(), 'cols': cols, 'q': q, 'key': key,
        'show_money': _can_see_money(request),
    })


@cms_required
def artists_list(request):   return _directory(request, 'artists')


@cms_required
def venues_list(request):    return _directory(request, 'venues')


@cms_required
def partners_list(request):  return _directory(request, 'partners')


@cms_required
def promoters_list(request): return _directory(request, 'promoters')


@cms_required
def suppliers_list(request): return _directory(request, 'suppliers')


@cms_required
def contacts_list(request):  return _directory(request, 'contacts')


# ── Calendar, Ticket Sales, Analysis, Data Health ───────────────────────────

@cms_required
def calendar(request):
    tenant = resolve_tenant(request)
    events = _events(request, tenant).exclude(date=None).order_by('date')
    return render(request, 'cms/veventa/calendar.html', {
        'events': events[:200],
        'notes': CalendarNote.objects.filter(tenant=tenant).order_by('date')[:200],
    })


@cms_required
def ticket_sales(request):
    tenant = resolve_tenant(request)
    if not _can_see_money(request):
        return render(request, 'cms/veventa/no_money.html', {})
    rows, revenue, charges, sold = [], 0.0, 0.0, 0
    for e in _events(request, tenant).financial():
        r = scoring._revenue(e)
        c = float(e.box_office_charges or 0)
        s = scoring._sold(e)
        revenue += r; charges += c; sold += s
        rows.append({'obj': e, 'revenue': r, 'charges': c, 'sold': s,
                     'alloc': scoring._alloc(e), 'pl': scoring._pl(e)})
    rows.sort(key=lambda x: x['sold'], reverse=True)
    return render(request, 'cms/veventa/tickets.html', {
        'rows': rows, 'revenue': revenue, 'charges': charges,
        'net': revenue - charges, 'sold': sold,
    })


@cms_required
def analysis(request):
    tenant = resolve_tenant(request)
    if not _can_see_money(request):
        return render(request, 'cms/veventa/no_money.html', {})
    by_year, by_brand, by_city = {}, {}, {}
    rows = []
    for e in _events(request, tenant).financial():
        pl = scoring._pl(e)
        rev = scoring._revenue(e)
        cost = scoring._cost(e)
        rows.append({'obj': e, 'pl': pl})
        for bucket, key in ((by_year, e.date.year if e.date else '—'),
                            (by_brand, e.brand.name if e.brand else '—'),
                            (by_city, e.city or '—')):
            b = bucket.setdefault(key, {'events': 0, 'rev': 0.0,
                                        'cost': 0.0, 'pl': 0.0})
            b['events'] += 1; b['rev'] += rev; b['cost'] += cost; b['pl'] += pl
    rows.sort(key=lambda x: x['pl'], reverse=True)
    return render(request, 'cms/veventa/analysis.html', {
        'by_year': sorted(by_year.items()),
        'by_brand': sorted(by_brand.items()),
        'by_city': sorted(by_city.items()),
        'best': rows[:5], 'worst': rows[-5:][::-1],
    })


@cms_required
def data_health(request):
    """Contradictions worth a human look.

    Each check is a rule, and each row says plainly what is wrong rather than
    repeating one generic sentence, which is what the old app did.
    """
    tenant = resolve_tenant(request)
    qs = _events(request, tenant)
    issues = []

    no_date = qs.filter(date=None).count()
    if no_date:
        issues.append(('No date', no_date, 'The event has no usable date, so it cannot be scored or reported.'))

    no_venue = qs.filter(venue=None).count()
    if no_venue:
        issues.append(('No venue', no_venue, 'No room chosen, so capacity and the venue score cannot be read.'))

    no_act = qs.filter(main_act=None).count()
    if no_act:
        issues.append(('No main act', no_act, 'The biggest single driver of the score is missing.'))

    over = [e for e in qs if e.venue and e.venue.capacity
            and scoring._alloc(e) > e.venue.capacity]
    if over:
        issues.append(('Tiers over capacity', len(over), 'More tickets are on sale than the room holds.'))

    sold_no_money = [e for e in qs.booked()
                     if scoring._sold(e) and not scoring._revenue(e)]
    if sold_no_money:
        issues.append(('Tickets sold, no revenue', len(sold_no_money), 'Tickets are marked sold but no money has been recorded against them.'))

    return render(request, 'cms/veventa/health.html', {'issues': issues})
