# encoding: utf-8
#
# Copyright (C) 2020 willipink.eu
# Author Moritz Münch moritzmuench@mailbox.org
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.


from datetime import datetime, date
from json import dumps

from django.http import HttpResponse, Http404
from django.template import loader
from django.template.defaultfilters import register

from .models import Country, Stamp, Coin, User


def index(request):
    ''' index view '''

    template = loader.get_template('coinc/index.html')
    countrys = Country.objects.order_by('name')
    users = User.objects.order_by('id')
    context = {
        'countrys': countrys,
        'users': users
    }

    return HttpResponse(template.render(context, request))


def detail_country(request, name_iso):
    ''' wrapper_view for a *single* country '''

    template = loader.get_template('coinc/country.html')
    try:
        country = Country.objects.get(name_iso=name_iso)
    except Country.DoesNotExist:
        raise Http404(f"Das Land '{name_iso}' ist nicht vorhanden")

    context = show_country(country, single_country=True)

    return HttpResponse(template.render(context, request))


@register.inclusion_tag('coinc/country.html')
def show_country(country, single_country=False):
    ''' detail_view for a country '''

    c = {
        'name_iso': country.name_iso,
        'name': country.name,
        'comment': country.comment,
        'years': {}
    }

    irregular_stamps = {}
    stamps = Stamp.objects.filter(country=country.name_iso).values(
        'name_short', 'years')

    if stamps:
        temp_stamps = {}

        for stamp in stamps:
            stamp_name_short = stamp['name_short']

            if stamp['years'] == "":
                temp_stamps[stamp_name_short] = {}
            else:
                for year in stamp['years'].split(","):
                    irregular_stamps[year] = {}
                    irregular_stamps[year][stamp_name_short] = {}
                temp_stamps[''] = {}

        stamps = temp_stamps
    else:
        stamps = {'': {}}

    year_now = datetime.now().year
    for year in [str(i) for i in range(country.euro_member_since, year_now + 1)]:
        stamps_per_year = stamps

        if irregular_stamps and year in irregular_stamps:
            for irregular_stamp, value in irregular_stamps[year].items():
                stamps_per_year[irregular_stamp] = {}

# TODO this is not a fix, when more stamps with uniqe years get added
        elif country.name_iso == 'gr':
            stamps_per_year = {'': {}}

        year_short = year[2:4]
        c['years'][year_short] = {}

        for stamp in stamps_per_year:
            c['years'][year_short][stamp] = {'name': stamp}

        stamps_per_year = {}


    coin_count = {'total': 0}
    for value in [1, 2, 5, 10, 20, 50, 100, 200, 201, 202, 203]:
        coin_count[value] = Coin.objects.filter(
                country=country,
                value=value
            ).exclude(found_by__isnull=True).count()
        if value > 200:
            coin_count['total'] += 200 * coin_count[value]
        else:
            coin_count['total'] += value * coin_count[value]
    coin_count['total'] /= 100

    return {
        'country': c,
        'single_country': single_country,
        'users': User.objects.order_by('id').values('name','color'),
        'total_coins': f"{coin_count['total']:.2f} €"
    }


@register.inclusion_tag('coinc/year.html')
def show_year(country, year):
    ''' show a year '''

    return {
        'year': country['years'][year],
        'year_short': year,
        'values': [1, 2, 5, 10, 20, 50, 100, 200, 201, 202, 203],
        'country': country['name_iso']
    }


@register.inclusion_tag('coinc/coin.html')
def show_coin(country, year, stamp, value):
    ''' show a coin
        @param country: Country.name_iso
        @param year: int YYYY
        @param stamp: stamp_id
        @param value: int e [1,2,5,10,20,50,100,200,201,202,203]
    '''
    year = int(year) # TODO fix this before the end of 2098
    if year == 99:
        year += 1900
    else:
        year += 2000

    if stamp == "":
        stamp = None

    try:
        coin = Coin.objects.get(country=country, year=year, stamp=stamp, value=value)
    except Coin.DoesNotExist:
        return {
            'value': value
        }
    except Coin.MultipleObjectsReturned:
        return {
            'value': value,
            'marker': 'Fehler!'
        }

    td_class = ''
    if not coin.exists:
        td_class = 'noexist'
    elif coin.buy_only:
        td_class = 'buy_only'
    elif coin.circulation and coin.circulation <= 1500000:
        td_class = 'rare'
    elif coin.value in [10, 20, 50]:
        td_class = 'brass'
    elif coin.found_by:
        td_class = 'found'
    else:
        td_class = 'exist'

    if coin.in_collector:
        td_class += ' ec'
    elif coin.checked:
        td_class += ' checked'

    div_class = ''
    marker = ''
    if coin.found_by:
        div_class = f"found {coin.found_by}"
        marker = '✔'

    name = coin.name if coin.name else ''
    special_class = ''
    special_name = ''
    if name:
        if value == 201:
            special_name = 'special1_name'
        elif value == 202:
            special_name = 'special2_name'
        elif value == 203:
            special_name = 'special3_name'

        if len(name) >= 18 and len(name) <= 42:
            special_class = 'two_lines'
        elif len(name) > 42:
            special_class = 'three_lines'

    return {
        'value': value,
        'marker': marker,
        'name': name,
        'td_class': td_class,
        'div_class': div_class,
        'special_class': special_class,
        'special_name': special_name
    }


def add_user(request, username, color):
    ''' add a user '''

    if username and color:
        try:
            existing_user = User.objects.get(name=username)
            return response(1, f"'{username}' ist schon vergeben.")

        except User.DoesNotExist:
            User.objects.create(name=username, color=color)
    else:
        return response(1, 'Du musst einen Namen und Farbe angeben.')

    return response()


def response(status=0, message=''):
    ''' return generic json-formatted status output with http-headers '''
    return HttpResponse(dumps({ 'status': status, 'message': message }))


def add_coin(request):
    ''' add or update a coin
        @params request.POST[field]

    *field*     mandatory       default         data type (internal) with limits
    ------------------------------------------------------------------------------------
    value       yes             -               int [1, 2, 5, 10, 20, 50, 100, 200, 201, 202, 203]
    year        yes             -               int [1999; 2098]
    country     yes             -               obj Country
    stamp       no              None            obj Stamp
    exists      no              False           bool
    name        no              -               str
    found_by    no              None            obj User
    found_on    no              None            str %Y-%m-%d
    circulation no              0               int [0; 1000000000]
    buy_only    no              False           bool
    checked     no              False           bool
    ec          no              False           bool

    '''

    # mandatory fields value, year, country
    try:
        value = int(request.POST['value'])
    except ValueError:
        return response(1, f"Der Münzwert '{value}' ist keine gültige Zahl.")
    if value not in [1, 2, 5, 10, 20, 50, 100, 200, 201, 202, 203]:
        return response(1, f"Der Münzwert '{value}' muss eine Zahl aus der Menge [1, 2, 5, 10, 20, 50, 100, 200, 201, 202, 203] sein.")

    try:
        year = int(request.POST['year'])
    except ValueError:
        return response(1, f"Das Jahr '{year}' ist keine gültige Zahl.")
    if year < 1999:
        return response(1, f"Das Jahr '{year}' ist kleiner als 1999.")
    if year > 2098:
        return response(1, f"Bitte den 2100-Jahrhundert-Datumsbug fixen. :-)")

    try:
        country = Country.objects.get(name_iso=request.POST['country'])
    except Country.DoesNotExist:
        return response(1, f"Das Land mit ISO-Kürzel '{name_iso}' existiert nicht.")

    # optional fields stamp, exists, name, found_by, found_on, circulation, buy_only, ec, checked
    stamp = request.POST['stamp']
    try:
        stamp = Stamp.objects.get(name_short=stamp)
    except Stamp.DoesNotExist:
        if stamp:
            return response(1, f"Die Prägerei '{stamp}' existiert nicht.")
        stamp = None

    exists = True if request.POST['exists'] == 'true' else False
    if not exists:
        name        = ''
        found_by    = None
        found_on    = date.today().strftime('%Y-%m-%d')
        circulation = 0
        buy_only    = False
        checked     = False
        ec          = False

    else:
        name = request.POST.get('name', '')
        try:
            name = str(name)
        except Exception as e:
            return response(1, f"Der Münzenname '{name}' ist kein gültiger Name. {e}")

        found_by = request.POST['found_by'] if request.POST['found_by'] else None
        if found_by:
            try:
                found_by = User.objects.get(name=found_by)
            except User.DoesNotExist:
                return response(1, f"Der Nutzer '{found_by}' existiert nicht.")

        found_on = request.POST['found_on'] if request.POST['found_on'] else None
        if found_on:
            try:
                found_on = datetime.strptime(found_on, '%d.%m.%Y')
                found_on = found_on.strftime('%Y-%m-%d')
            except ValueError:
                return response(1, f"Das Datum '{found_on}' entspricht nicht dem Format 'DD.MM.YYYY'.")
        else:
            found_on = date.today().strftime('%Y-%m-%d')

        circulation = request.POST['circulation'] if request.POST['circulation'] else 0
        try:
            circulation = int(circulation.replace('.', ''))
        except ValueError:
            return response(1, f"Die Münzauflage '{circulation}' muss eine Zahl sein.")
        if circulation < 0:
            return response(1, f"Die Münzauflage '{circulation}' muss 0 oder positiv sein.")
        elif circulation > 1000000000:
            return response(1, f"Die Münzauflage '{circulation}' ist unrealistisch.")

        buy_only = True if request.POST['buy_only'] == 'true' else False
        checked  = True if request.POST['checked']  == 'true' else False
        ec       = True if request.POST['ec']       == 'true' else False

    try:
        coin = Coin.objects.get(
            value=value,
            year=year,
            country=country,
            stamp=stamp)
        coin.name         = name
        coin.found_by     = found_by
        coin.found_on     = found_on
        coin.circulation  = circulation
        coin.buy_only     = buy_only
        coin.checked      = checked
        coin.in_collector = ec
        coin.exists       = exists
        coin.save(force_update=True)

    except Coin.DoesNotExist:
        Coin.objects.create(
            value        = value,
            year         = year,
            country      = country,
            stamp        = stamp,
            name         = name,
            found_by     = found_by,
            found_on     = found_on,
            circulation  = circulation,
            buy_only     = buy_only,
            checked      = checked,
            in_collector = ec,
            exists       = exists)

    return response()


def statistic(request):
    ''' show statistics '''

    return HttpResponse('Statistik')
