import os

from django.apps import apps
from django.http import Http404, JsonResponse
from django.shortcuts import render

from . import engine
from .engine import DICE_SIDES, ever_stats

NOHIST_TRUE_VALUES = {'T', 'true', 'True', 'TRUE'}
HISTORY_LIMIT = 11


def _static_version(*parts):
    """mtime of a static file, used to cache-bust <script>/<link> tags so
    browsers pick up edits during development without a manual hard refresh."""
    path = os.path.join(apps.get_app_config(
        'dice').path, 'static', 'dice', *parts)
    try:
        return int(os.path.getmtime(path))
    except OSError:
        return 0


def _session_stats(request):
    stats = request.session.get('stats')
    if stats is None:
        stats = {
            dice_type: {str(face): 0 for face in range(1, sides + 1)}
            for dice_type, sides in DICE_SIDES.items()
        }
        request.session['stats'] = stats
    return stats


def _state(request):
    return {
        'session_stats': _session_stats(request),
        'ever_stats': ever_stats(),
        'history': request.session.get('stack', []),
        'numberrolls': request.session.get('numberrolls', 0),
        'paydirt_off': request.session.get('paydirt_off'),
        'paydirt_def': request.session.get('paydirt_def'),
    }


HELP_NUMBER_DICE = [
    ('d4numr - Number', 'd4_numr', '5d4numr'),
    ('d6numr - Number', 'd6_numr', '5d6numr'),
    ('d8numr - Number', 'd8_numr', '5d8numr'),
    ('d10numr - Number', 'd10_numr', '5d10numr'),
    ('d12numr - Number', 'd12_numr', '5d12numr'),
    ('d20numr - Number', 'd20_numr', '5d20numr'),
]
HELP_D6_DICE = [
    ('d6pips - Pips', 'd6_pips', '5d6pips'),
    ('d6cpip - Colored Pips', 'd6_cpip', '5d6cpip'),
    ('d6memr - Memoir', 'd6_memr', '5d6memr'),
    ('d6quid - Quarriors Quidity', 'd6_quid', '5d6quid'),
    ('d6pdrt - AH Paydirt', 'd6_pdrt', '5d6pdrt'),
    ("d6dlds - Don't L.L.A.M.A. Dice", 'd6_dlds', '3d6dlds'),
    ('d6rfdw - Run, Fight, Die! White', 'd6_rfdw', '5d6rfdw'),
    ('d6rfdg - Run, Fight, Die! Grey', 'd6_rfdg', '5d6rfdg'),
    ('d6rfdb - Run, Fight, Die! Black', 'd6_rfdb', '5d6rfdb'),
    ('d6rfdp - Run, Fight, Die! Pink', 'd6_rfdp', '5d6rfdp'),
    ('d6dddr - D-Day Dice Red', 'd6_dddr', '5d6dddr'),
    ('d6dddb - D-Day Dice Blue', 'd6_dddb', '5d6dddb'),
    ('d6dddw - D-Day Dice White', 'd6_dddw', '5d6dddw'),
]
HELP_DICE_COLORED = [
    ('d10numr - Number, Colored',
     'Orange background, Black edge, Grey number (5d10numr#FFA500#000000#808080)',
     'd10_color', '5d10numr#FFA500#000000#808080'),
    ('d6pips - Pips, Colored',
     'Forest Green background and edge, White pips (5d6pips#228B22#228B22#FFFFFF)',
     'd6_color', '5d6pips#228B22#228B22#FFFFFF'),
]
HELP_PREVIEW_GROUPS = {
    'number': [(div_id, dshort) for _label, div_id, dshort in HELP_NUMBER_DICE],
    'd6': [(div_id, dshort) for _label, div_id, dshort in HELP_D6_DICE],
    'colors': [(div_id, dshort) for _label, _note, div_id, dshort in HELP_DICE_COLORED],
}


def _pct(count, total):
    return f'{count * 100 / total:.2f}' if total else '0.00'


def _dice_face_rows(session_stats, ever_stats):
    rows = []
    for dice_type, sides in DICE_SIDES.items():
        session_total = sum(session_stats[dice_type].values())
        ever_total = sum(ever_stats[dice_type].values())
        faces = []
        for face in range(1, sides + 1):
            key = str(face)
            session_count = session_stats[dice_type].get(key, 0)
            ever_count = ever_stats[dice_type].get(key, 0)
            faces.append((
                face, session_count, _pct(session_count, session_total),
                ever_count, _pct(ever_count, ever_total),
            ))
        rows.append((dice_type, faces))
    return rows


def index(request):
    state = _state(request)
    history = state['history']
    history_display = [html.replace('F die', 'H die')
                       for html in list(reversed(history))[:10]]
    context = {
        **state,
        'current_roll': history[-1] if history else None,
        'history_display': history_display,
        'dice_face_range': _dice_face_rows(state['session_stats'], state['ever_stats']),
        'dice_css_version': _static_version('css', 'dice.css'),
        'dice_js_version': _static_version('js', 'dice.js'),
        'help_number_dice': [(label, div_id) for label, div_id, _dshort in HELP_NUMBER_DICE],
        'help_d6_dice': [(label, div_id) for label, div_id, _dshort in HELP_D6_DICE],
        'help_dice_colored': [
            (label, note, div_id)
            for label, note, div_id, _dshort in HELP_DICE_COLORED
        ],
    }
    return render(request, 'dice/index.html', context)


def help_examples(request, group):
    preview_group = HELP_PREVIEW_GROUPS.get(group)
    if preview_group is None:
        raise Http404

    examples = []
    increments = []
    for div_id, dshort in preview_group:
        result = engine.roll_dshort(dshort, track_stats=False)
        examples.append({'div_id': div_id, 'roll_html': result['roll_html']})
        increments.extend(result['increments'])

    session_stats = _session_stats(request)
    for dice_type, face in increments:
        key = str(face)
        session_stats[dice_type][key] = session_stats[dice_type].get(
            key, 0) + 1
    request.session['stats'] = session_stats

    engine.increment_ever(increments)
    return JsonResponse({
        'examples': examples,
        'session_stats': session_stats,
        'ever_stats': engine.ever_stats(),
    })


def roll(request):
    dshort = request.GET.get('dshort', '').strip()
    nohist = request.GET.get('nohist', '').strip()
    track = nohist not in NOHIST_TRUE_VALUES

    result = engine.roll_dshort(dshort)

    if not track:
        return JsonResponse({'roll_html': result['roll_html'], 'dshort': result['dshort']})

    if result['dnum'] > 0:
        stats = _session_stats(request)
        for dice_type, face in result['increments']:
            key = str(face)
            stats[dice_type][key] = stats[dice_type].get(key, 0) + 1
        request.session['stats'] = stats

        stack = request.session.get('stack', [])
        stack.append(result['roll_html'])
        if len(stack) > HISTORY_LIMIT:
            stack = stack[-HISTORY_LIMIT:]
        request.session['stack'] = stack
        request.session['numberrolls'] = len(stack)

        if result['paydirt_off'] is not None:
            request.session['paydirt_off'] = result['paydirt_off']
            request.session['paydirt_def'] = result['paydirt_def']

    return JsonResponse({
        'roll_html': result['roll_html'],
        'dshort': result['dshort'],
        **_state(request),
    })
