from django.shortcuts import render
from django.http import HttpResponse
from django.template.response import TemplateResponse
from django.shortcuts import redirect
from django.views.decorators.csrf import csrf_exempt
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.models import User as DjangoUser
from django.contrib.auth import authenticate
from django.contrib.auth import login as auth_login, logout as auth_logout
from django.core.mail import send_mail, mail_admins, EmailMessage
from django.db.utils import IntegrityError
from django.db import transaction
from django.utils.datastructures import MultiValueDictKeyError
from datetime import date, datetime, timedelta
from dateutil.relativedelta import relativedelta
from weasyprint import HTML
from urllib.parse import parse_qs
import requests
import json
import glob
import PIL.Image
import random
import itertools
from collections import OrderedDict

from .models import (
    User, ALKATOR_CHOICES_DICT, ALKATOR_CLASSES,
    Profile, Racer, Invoice, Product, InvoiceProduct,
    Cart, CartProduct
)
from alkator.settings import COMGATE_MERCHANT, COMGATE_SECRET, COMGATE_TEST


DEADLINE = date(2025, 10, 5)
ALKATOR_CLASS = 3


@csrf_exempt
def register_user(request):
    if not request.POST['first_name']:
        return HttpResponse('{"reason":"Jméno je povinné!"}', status=400, content_type='application/json')
    if not request.POST['last_name']:
        return HttpResponse('{"reason":"Přijmení je povinné!"}', status=400, content_type='application/json')
    if not request.POST['email']:
        return HttpResponse('{"reason":"Email je povinný!"}', status=400, content_type='application/json')
    if not request.POST['address']:
        return HttpResponse('{"reason":"Adresa je povinná!"}', status=400, content_type='application/json')
    if not request.POST['password1'] or not request.POST['password2']:
        return HttpResponse('{"reason":"Heslo je povinné!"}', status=400, content_type='application/json')
    if request.POST['password1'] != request.POST['password2']:
        return HttpResponse('{"reason":"Hesla se neshodují!"}', status=400, content_type='application/json')
    if not request.POST['phone']:
        return HttpResponse('{"reason":"Telefon je povinný"}', status=400, content_type='application/json')
    if DjangoUser.objects.filter(email=request.POST['email']):
        return HttpResponse('{"reason":"Email je již registrován!"}', status=400, content_type='application/json')
    email = request.POST['email']

    user = DjangoUser.objects.create_user(
        email, email, request.POST['password1']
    )
    profile = Profile(
        user=user,
        first_name=request.POST['first_name'],
        last_name=request.POST['last_name'],
        address=request.POST['address'],
        phone=request.POST['phone'],
    )
    profile.save()
    auth_login(request, user)
    return HttpResponse('{"success":"Úspěšná registrace!", "redirect":"/#"}', content_type='application/json')


@csrf_exempt
def login(request):
    try:
        user = authenticate(request, username=request.POST['email'], password=request.POST['password'])
    except MultiValueDictKeyError:
        return HttpResponse('{"reason":"Nezadané jméno nebo heslo!"}', status=400, content_type='application/json')
    if user is not None:
        auth_login(request, user)
        return HttpResponse('{"success":"Úspěšně přihlášen uživatel '+ user.email + '", "redirect":"/#"}', content_type='application/json')
    else:
        return HttpResponse('{"reason":"Nesprávné jméno nebo heslo!"}', status=400, content_type='application/json')


@csrf_exempt
def logout(request):
    auth_logout(request)
    return redirect("/#")


@csrf_exempt
def register_racer(request):
    if not request.user.is_authenticated:
        return HttpResponse('{"reason":"Je potřeba se přihlásit!"}', status=400, content_type='application/json')
    if date.today() >= DEADLINE:
        return HttpResponse('{"reason":"Too late!"}', status=400, content_type='application/json')
    if not request.POST.get('agreement'):
        return HttpResponse('{"reason":"Je potřeba souhlasit se zpracováním údajů!"}', status=400, content_type='application/json')
    if not request.POST['first_name']:
        return HttpResponse('{"reason":"Jméno je povinné!"}', status=400, content_type='application/json')
    if not request.POST['last_name']:
        return HttpResponse('{"reason":"Přijmení je povinné!"}', status=400, content_type='application/json')
    try:
        dob = datetime.strptime(request.POST['date_of_birth'], "%Y-%m-%d").date()
        if dob > DEADLINE - relativedelta(years=18):
            return HttpResponse('{"reason":"Je potřeba mít 18 let v den závodu!"}', status=400, content_type='application/json')
        elif dob < DEADLINE - relativedelta(years=100):
            return HttpResponse('{"reason":"Opravdu vám je 100 let?"}', status=400, content_type='application/json')
    except:
        return HttpResponse('{"reason":"Špatný formát datu narození!"}', status=400, content_type='application/json')

    invoice_id = Invoice.next_invoice_id()

    profile = request.user.profile
    user = request.user

    product = Product.objects.get(id=1)
    price = product.price

    if product.quantity <= 0:
        return HttpResponse('{"reason":"Jsme vyprodaní!"}', status=400, content_type='application/json')


    invoice = Invoice(
        invoice_id=invoice_id,
        user=user,
        total_price=price,
        address="",
    )
    racer = Racer(
        product=product,
        invoice=invoice,
        quantity=1,
        profile = profile,
        first_name = request.POST['first_name'],
        last_name = request.POST['last_name'],
        email = request.POST['email'],
        team = request.POST['team'],
        phone = request.POST['phone'],
        date_of_birth = dob,
        alkator_class = ALKATOR_CLASS,
        price=price,
    )
    invoice.save()
    racer.save()

    payment_data = {
        'merchant': COMGATE_MERCHANT,
        'test': 'true' if COMGATE_TEST else 'false',
        'price': price * 100,
        'curr': 'CZK',
        'method': 'ALL',
        'label': 'Startovné na závod Alkátor Race Dolní Čermná 2025',
        'email': user.email,
        'fullName': f"{profile.first_name} {profile.last_name}",
        'refId': f'{invoice.invoice_id}',
        'secret': COMGATE_SECRET,
        'prepareOnly': 'true',
    }
    result = requests.post('https://payments.comgate.cz/v1.0/create', data=payment_data)

    result = parse_qs(result.text)

    if result['code'][0] != '0':
        racer.delete()
        invoice.delete()
        return HttpResponse('{"reason":"Chyba na straně platební brány: ' + result['message'][0] + ', zkuste prosím registraci později."}', status=400, content_type='application/json')

    product.quantity -= 1
    product.save()

    invoice.trans_id = result['transId'][0]
    invoice.save()

    return HttpResponse('{"success":"", "redirect":"' + result['redirect'][0] + '"}', content_type='application/json')


def login_status(request):
    if not request.user.is_authenticated:
        return HttpResponse('{}', content_type='application/json')

    user = request.user
    racers = user.profile.racers.all()
    return HttpResponse(json.dumps({
        "email": user.email,
        "first_name": user.profile.first_name,
        "last_name": user.profile.last_name,
        "address": user.profile.address,
        "racers": [{
            "id": racer.invoice_id,
            "first_name": racer.first_name,
            "last_name": racer.last_name,
            "email": racer.email,
            "phone": racer.phone,
            "team": racer.team,
            "date_of_birth": racer.date_of_birth.strftime("%Y-%m-%d"),
            "paid": racer.invoice.paid,
        } for racer in racers]
    }), content_type='application/json')


@csrf_exempt
def change_racer(request):
    try:
        racer = Racer.objects.get(invoice_id=request.POST['id'])
        if request.user != racer.profile.user:
            return HttpResponse('{"reason":"Nedostatečná práva!"}', status=400, content_type='application/json')
        if date.today() >= DEADLINE:
            return HttpResponse('{"reason":"Příliš pozdě na změnu účastníka!"}', status=400, content_type='application/json')
        if not request.POST['first_name']:
            return HttpResponse('{"reason":"Jméno je povinné!"}', status=400, content_type='application/json')
        if not request.POST['last_name']:
            return HttpResponse('{"reason":"Přijmení je povinné!"}', status=400, content_type='application/json')
        try:
            dob = datetime.strptime(request.POST['date_of_birth'], "%Y-%m-%d").date()
            if dob > DEADLINE - relativedelta(years=18):
                return HttpResponse('{"reason":"Je potřeba mít 18 let v den závodu!"}', status=400, content_type='application/json')
            elif dob < DEADLINE - relativedelta(years=100):
                return HttpResponse('{"reason":"Opravdu vám je 100 let?"}', status=400, content_type='application/json')
        except:
            return HttpResponse('{"reason":"Špatný formát datu narození!"}', status=400, content_type='application/json')
        racer.first_name = request.POST['first_name']
        racer.last_name = request.POST['last_name']
        racer.email = request.POST['email']
        racer.phone = request.POST['phone']
        racer.team = request.POST['team']
        racer.date_of_birth = dob
        racer.save()
        return HttpResponse('{"success":"Úspěšně uloženo."}', content_type='application/json')
    except MultiValueDictKeyError:
        return HttpResponse('{"reason":"Nějaký údaj chybí!"}', status=400, content_type='application/json')
    except Racer.DoesNotExist:
        return HttpResponse('{"reason":"Upravujete neexistujícího závodníka!"}', status=400, content_type='application/json')


@csrf_exempt
def payment_result(request):
    result = parse_qs(request.body.decode('utf8'))
    ref_id = int(result['refId'][0])
    paid = result['status'][0]
    secret_match = result['secret'][0] == COMGATE_SECRET
    test = result['test'][0] != 'false'
    if not secret_match or test != COMGATE_TEST:
        return HttpResponse(status=400)
    try:
        invoice = Invoice.objects.get(invoice_id=ref_id)
        user = invoice.user
    except Invoice.DoesNotExist:
        mail_admins('Chyba s platbou!', f'invoice_id={ref_id}&paid={paid}')
        return HttpResponse(status=404)
    if paid == 'PAID':
        invoice.paid = True
        invoice.save()
        try:
            racer = Racer.objects.get(invoice=invoice)
            mail = EmailMessage(
                subject=f"úspěšná registrace do závodu Alkátor race Dolní Čermná ({racer.first_name} {racer.last_name})",
                body=f"""Zdravím tě Alkátore,
toto je potvrzovací email o účasti v nezapomenutelném závodě Alkátor-race.
Prosíme kontrolujete si své hromadné schránky a spamy.

Závod se koná 5.10.2024 v Dolní Čermné.
Zajištěno je stanování, občerstvení a také večerní after párty.

Připrav na zkoušku své tělo, játra a tvé mozkové buňky.
Budou tě čekat nevídané překážky.

O podrobnostech tě budeme informovat emailem.
Nezapomeň nás sledovat na Facebooku, Instagramu, YouTube a TikToku.

Na tento email není třeba odpovídat, protože je generován automaticky s přijatou platbou. V případě potřeby pište na info@alkator.cz .

ALKÁTOR TEAM
email: info@alkator.cz
tel: + 420 728 018 088
web: https://alkator.cz""",
                from_email="info@alkator.cz",
                to=[invoice.user.email, racer.email] if racer.email and invoice.user.email != racer.email else [invoice.user.email],
                bcc=["info@alkator.cz"],
                cc=[]
            )
        except Racer.DoesNotExist:
            mail = EmailMessage(
                subject=f"úspěšně zaplacený nákup v Alkátor shopu",
                body=f"""Zdravím tě Alkátore,

úspěšně si zakoupil merch na Alkátor shopu.

Na tento email není třeba odpovídat, protože je generován automaticky s přijatou platbou. V případě potřeby pište na info@alkator.cz .

ALKÁTOR TEAM
email: info@alkator.cz
tel: + 420 728 018 088
web: https://alkator.cz""",
                from_email="info@alkator.cz",
                to=[invoice.user.email],
                bcc=["info@alkator.cz"],
                cc=[]
            )

        template = TemplateResponse(
            None,
            'invoice.html',
            {
                'user': user,
                'invoice': invoice,
                'products': InvoiceProduct.objects.filter(invoice=invoice),
                'paid_date': invoice.paid_date + timedelta(days=1),
            }
        )
        template.render()
        pdf_name = f"invoices/{invoice.invoice_id}_{user.profile.last_name}_{user.profile.first_name}.pdf"
        HTML(string=template.content).write_pdf(pdf_name)

        attach = open(pdf_name, 'rb')
        mail.attach('faktura.pdf', attach.read(), 'application/pdf')
        if not mail.send():
            return HttpResponse(status=500)
    elif paid == 'CANCELLED' and not invoice.paid:
        for ip in InvoiceProduct.objects.filter(invoice=invoice):
            ip.product.quantity += ip.quantity
            ip.product.save()
            ip.delete()
        invoice.delete()
    return HttpResponse(status=200)


def payment_state(request):
    invoice_id = request.GET['refId']
    try:
        if Invoice.objects.get(invoice_id=invoice_id).paid:
            return HttpResponse('{"status":"success", "reason":"Úspěšná platba"}', content_type='application/json')
        else:
            return HttpResponse('{"status":"failed", "reason":"Zatím nemáme informace o provedené platbě. Zkuste reload nebo zkontrolujte email."}', content_type='application/json')
    except Invoice.DoesNotExist:
        return HttpResponse('{"status":"failed", "reason":"Závodník neexistuje, registraci závodníka prosím opakujte."}', content_type='application/json')


def products(request):
    return HttpResponse(json.dumps([
        {
            'id': product.id,
            'name': product.name,
            'description': product.description,
            'img': product.img.url,
            'price': product.price,
            'quantity': product.quantity,
        }
        for product in Product.objects.all() if product.quantity > 0 and not product.hidden
    ]), content_type='application/json')


def cart_add(request):
    _id = request.GET['id']
    user = request.user
    if not user.is_authenticated:
        return HttpResponse('{"status": "failed", "reason": "Pro využivání Košíku je třeba se přihlásit!"}')
    try:
        cart = user.cart
    except AttributeError:
        cart = Cart(user=user)
        cart.save()
    product = Product.objects.get(id=_id)
    try:
        cart_product = CartProduct(
            product=product,
            cart=cart,
            quantity=1,
        )
        cart_product.save()
    except IntegrityError as e:
        cart_product = CartProduct.objects.get(cart=cart, product=product)
        cart_product.quantity += 1
        if cart_product.quantity > product.quantity:
            return HttpResponse('{"status": "failed", "reason": "Nemáme dostatek předmětů na skladě!"}')
        cart_product.save()
    return HttpResponse('{"status":"success", "reason":"Úspěšně přidáno do košíku."}', status=200)


def select_delivery(request):
    user = request.user
    cart = user.cart
    cart.address = request.GET['delivery']
    cart.save()
    return HttpResponse('{"status":"success", "reason":"Úspěšně vybraná zásilkovna."}', status=200)


def delivery(request):
    try:
        if request.user.cart.address:
            return HttpResponse('"' + request.user.cart.address + '"', status=200, content_type='application/json')
        return HttpResponse("undefined", status=200, content_type='application/json')
    except AttributeError:
        return HttpResponse("undefined", status=200, content_type='application/json')


@csrf_exempt
@transaction.atomic
def cart_buy(request):
    cart = request.user.cart
    user = request.user
    profile = user.profile
    cart_products = CartProduct.objects.filter(cart=cart)

    for cp in cart_products:
        if cp.quantity > cp.product.quantity:
            return HttpResponse('{"status": "failed", "reason": "Nemáme dostatek ' + cp.product.name + ' na skladě!"}', status=400, content_type='application/json')

    if not cart.address:
        return HttpResponse('{"status": "failed", "reason": "Je potřeba vybrat výběrní místo!"}', status=400, content_type='application/json')

    invoice = Invoice(
        user=user,
        address=cart.address,
        invoice_id=Invoice.next_invoice_id(),
        total_price=0,
    )
    total_price = 0
    invoice.save()

    for cp in itertools.chain(cart_products, [CartProduct(quantity=1, product=Product.objects.get(id=2), cart=cart)]):
        ip = InvoiceProduct(
            invoice=invoice,
            price=cp.product.price,
            quantity=cp.quantity,
            product=cp.product,
        )
        cp.product.quantity -= cp.quantity
        cp.product.save()
        total_price += cp.quantity * cp.product.price
        ip.save()

    if total_price <= 79:
        return HttpResponse('{"status": "failed", "reason": "Prosím přidejte svoje položky do košíku!"}', status=400, content_type='application/json')

    payment_data = {
        'merchant': COMGATE_MERCHANT,
        'test': 'true' if COMGATE_TEST else 'false',
        'price': total_price * 100,
        'curr': 'CZK',
        'method': 'ALL',
        'label': 'Merch Alkátor Race',
        'email': user.email,
        'fullName': f"{profile.first_name} {profile.last_name}",
        'refId': f'{invoice.invoice_id}',
        'secret': COMGATE_SECRET,
        'prepareOnly': 'true',
    }
    result = requests.post('https://payments.comgate.cz/v1.0/create', data=payment_data)

    result = parse_qs(result.text)

    if result['code'][0] != '0':
        for ip in InvoiceProduct.objects.filter(invoice=invoice):
            ip.delete()
        invoice.delete()
        return HttpResponse('{"reason":"Chyba na straně platební brány: ' + result['message'][0] + ', zkuste prosím nákup později."}', status=400, content_type='application/json')

    invoice.total_price = total_price
    invoice.trans_id = result['transId'][0]
    cart.delete()
    invoice.save()

    return HttpResponse('{"success":"", "redirect":"' + result['redirect'][0] + '"}', content_type='application/json')


def cart_delete(request):
    _id = request.GET['id']
    user = request.user
    cart = user.cart
    product = Product.objects.get(id=_id)
    cart_product = CartProduct.objects.get(cart=cart, product=product)
    cart_product.delete()
    return HttpResponse('{"status":"success", "reason":"Úspěšně odstraněno z košíku."}', content_type='application/json')


def cart_decrease(request):
    _id = request.GET['id']
    user = request.user
    cart = user.cart
    product = Product.objects.get(id=_id)
    cart_product = CartProduct.objects.get(cart=cart, product=product)
    cart_product.quantity -= 1
    if cart_product.quantity == 0:
        cart_product.delete()
    else:
        cart_product.save()
    return HttpResponse('{"status":"success", "reason":"Úspěšně sníženo množství v košíku."}', content_type='application/json')


def cart(request):
    user = request.user
    if not user.is_authenticated:
        return HttpResponse("[]", content_type='application/json')
    cart = []
    try:
        for cart_product in CartProduct.objects.filter(cart=user.cart):
            cart.append({
                'id': cart_product.product.id,
                'name': cart_product.product.name,
                'description': cart_product.product.description,
                'quantity': cart_product.quantity,
                'price': cart_product.product.price,
                'img': cart_product.product.img.url,
            })
    except AttributeError:
        return HttpResponse("[]", content_type='application/json')
    return HttpResponse(json.dumps(cart))


def results(request):
    results = []
    n = 1
    for user in User.objects.filter(alkator_class=2).order_by('duration'):
        if user.duration:
            if user.alkator_category == 1:
                order = f'{n}.'
                n += 1
            else:
                order = 'x.'
            results.append({
                'order': order,
                'duration': str(user.duration),
                'alkator_category': ALKATOR_CHOICES_DICT[user.alkator_category],
                'starting_number': user.starting_number,
            })
    for user in User.objects.filter(alkator_class=2).order_by('duration'):
        if not user.duration and user.starting_number:
            results.append({
                'order': 'DNF',
                'duration': 'Nedokončil',
                'starting_number': user.starting_number,
                'alkator_category': ''
            })

    return HttpResponse(json.dumps(results), content_type='application/json')


def photos(request):
    rtn = []
    for (i, name) in ALKATOR_CLASSES[:2]:
        photos = {}
        rtn.append(photos)
        photos['name'] = name
        photos['index'] = i
        photos['photos'] = []
        files = glob.glob(f"photos/{i}/*.jpg")
        random.shuffle(files)
        for file in files:
            img = PIL.Image.open(file)
            photos['photos'].append({
                'original': '/' + file.replace(".jpg", ".webp"),
                'thumbnail': '/' + file.replace(f'photos/{i}/', f'photos/{i}/thumbnail/').replace(".jpg", ".webp"),
                'original_width': img.width,
                'original_height': img.height,
            })

    return HttpResponse(json.dumps(rtn), content_type='application/json')


@csrf_exempt
def upload_files(request):
    for file in request.FILES.getlist('files'):
        rand = random.randint(0, 99999)
        random_name = f'photos/uploads/{rand}_{file.name}'
        with open(random_name, "wb+") as destination:
            for chunk in file.chunks():
                destination.write(chunk)
    return HttpResponse('{"success":"Úspěšně nahráno! Počkejte až dojde ke schválení nahraných souborů."}', content_type='application/json')


@staff_member_required
def invoice(request):
    invoice = Invoice.objects.get(invoice_id=request.GET['invoice_id'])
    user = DjangoUser.objects.get(id=invoice.user_id)
    return TemplateResponse(
        None,
        'invoice.html',
        {
            'user': user,
            'invoice': invoice,
            'products': InvoiceProduct.objects.filter(invoice=invoice),
            'paid_date': invoice.paid_date + timedelta(days=1),
        }
    )
