import io
from csv import DictReader
from dataclasses import dataclass
from datetime import datetime, date
from decimal import Decimal
from enum import Enum
from typing import List, Dict

from urllib.parse import urlencode

from ludialudom.errors import SignatureError
from ludialudom.resources.base import BaseResource


class Payment(BaseResource):
    class PaymentState(Enum):
        ACTIVE = 1
        SUSPENDED = 2
        CANCELED = 3
        INVALID_CARD = 4

    @dataclass
    class TransactionItem(object):
        project_code: int
        widget: str
        requested_at: datetime
        amount: Decimal
        vs: str
        name: str
        surname: str
        email: str
        payment_type_id: int
        payment_type: str
        transaction_state_id: int
        transaction_state: str
        state: str
        serial_number: int

    @dataclass
    class SlipItem(object):
        appeal: int
        transaction: int
        transaction_amount: Decimal
        donation_amount: Decimal
        payed_at: datetime
        vs: str
        name: str
        surname: str
        email: str
        widget: str
        serial_number: int

    """
    Method returns redirect URL for payment
    """
    def create_payment_url(self, payment_id: int, project_code: int, channel: int, amount: Decimal, name: str,
                           surname: str, email: str, start_date: datetime = None, end_date: datetime = None,
                           is_mobile: bool = False) -> str:
        payload = {
            'clientId': self._configuration.client_id,
            'paymentId': payment_id,
            'projectCode': project_code,
            'paymentChannel': channel,
            'amount': amount,
            'name': name,
            'surname': surname,
            'email': email,
            'mobileDevice': 'true' if is_mobile else 'false',
            'sign': self._sign(f"{payment_id}{project_code}{channel}{amount}{name}{surname}{email}")
        }

        if start_date:
            payload['startDate'] = start_date.strftime("%Y-%m-%d %H:%M:%S")

        if end_date:
            payload['endDate'] = end_date.strftime("%Y-%m-%d %H:%M:%S")

        return f"{self._configuration.environment.web_url}/pay?{urlencode(payload)}"

    def change_amount(self, payment_id: int, amount: Decimal) -> bool:
        payload = {
            'paymentId': payment_id,
            'newAmount': amount,
            'sign': self._sign(f"{payment_id}{amount}")
        }

        response = self.requestor.request('POST', '/changeComfortPay', payload=payload)

        if response.get('sign') != self._sign(f"{response.get('result')}{response.get('code')}"):
            raise SignatureError()

        return 'OK' == response.get('result')

    def change_state(self, payment_id: int, state: PaymentState) -> PaymentState:
        payload = {
            'paymentId': payment_id,
            'status': state.value,
            'sign': self._sign(f"{payment_id}{state.value}")
        }

        response = self.requestor.request('POST', '/setComfortPayStatus', payload=payload)
        response_sign = self._sign(f"{response.get('result')}{response.get('code')}{response.get('status')}")

        if response.get('sign') != response_sign:
            raise SignatureError()

        return Payment.PaymentState(response['status'])

    def get_state(self, payment_id: int) -> PaymentState:
        payload = {
            'paymentId': payment_id,
            'sign': self._sign(str(payment_id))
        }

        response = self.requestor.request('POST', '/getComfortPayStatus', payload=payload)
        response_sign = self._sign(f"{response.get('result')}{response.get('code')}{response.get('status')}")

        if response.get('sign') != response_sign:
            raise SignatureError()

        return Payment.PaymentState(response['status'])

    def transactions(self, project_code: int, date_from: date = None, date_to: date = None) -> List[TransactionItem]:
        payload = {
            'projectCode': project_code,
            'sign': self._sign(f"{project_code}")
        }

        if date_from is not None:
            payload['from'] = date_from.strftime("%Y-%m-%d")

        if date_to is not None:
            payload['to'] = date_to.strftime("%Y-%m-%d")

        response = self.requestor.request('GET', '/GetPayments', query=payload, parse=False)
        reader = DictReader(io.StringIO(response), delimiter=';')

        result = []
        for item in reader:
            result.append(self.TransactionItem(
                project_code=int(item['projectCode']),
                widget=item[' id_platba_widget_ext'],
                requested_at=datetime.strptime(item[' d_zadania'], "%Y-%m-%d %H:%M:%S"),
                amount=Decimal(item[' i_suma'].replace(",", ".")),
                vs=item[' s_vs'],
                name=item[' s_meno'],
                surname=item[' s_priezvisko'],
                email=item[' s_email'],
                payment_type_id=int(item[' id_transakcia_sposob']),
                payment_type=item[' s_transakcia_sposob'],
                transaction_state_id=int(item[' id_transakcia_stav']),
                transaction_state=item[' s_transakcia_stav'],
                state=item[' s_stav'],
                serial_number=int(item[' i_poradove_cislo'])
            ))

        return result

    def slips(self, project_code: int, random: int = 868) -> List[SlipItem]:
        payload = {
            'projectCode': project_code,
            'random': random,
            'sign': self._sign(f"{random}")
        }

        response = self.requestor.request('GET', '/GetRecipientPayments', query=payload, parse=False)
        reader = DictReader(io.StringIO(response), delimiter=';')

        result = []
        for item in reader:
            result.append(self.SlipItem(
                appeal=int(item['s_kod_vyzvy']),
                transaction=int(item['id_transakcia']),
                transaction_amount=Decimal(item['i_suma_transakcie'].replace(",", ".")),
                payed_at=datetime.strptime(item['d_uhrady'], "%Y-%m-%d %H:%M:%S"),
                vs=item['s_vs'],
                donation_amount=Decimal(item['i_suma_daru'].replace(",", ".")),
                name=item['s_meno'],
                surname=item['s_priezvisko'],
                email=item['s_email'],
                widget=item['id_platba_widget_ext'],
                serial_number=int(item['i_poradove_cislo'])
            ))

        return result
