import logging
import os
import json
from datetime import datetime, timezone
from PySide6.QtWidgets import QApplication
from jal.constants import Setup
from jal.db.settings import JalSettings


FOREIGN_SECTION = "CurrencyIncomeList"
# ----------------------------------------------------------------------------------------------------------------------
class Ru_NDFL3:
    currencies = {
        'AUD': {'code': '036', 'name': 'Австралийский доллар', 'multiplier': 100},
        'AZN': {'code': '944', 'name': 'Азербайджанский манат', 'multiplier': 100},
        'GBP': {'code': '826', 'name': 'Фунт стерлингов', 'multiplier': 100},
        'AMD': {'code': '051', 'name': 'Армянский драм', 'multiplier': 10000},
        'BYN': {'code': '933', 'name': 'Белорусский рубль', 'multiplier': 100},
        'BGN': {'code': '975', 'name': 'Болгарский лев', 'multiplier': 100},
        'BRL': {'code': '986', 'name': 'Бразильский реал', 'multiplier': 100},
        'HUF': {'code': '348', 'name': 'Форинт', 'multiplier': 10000},
        'HKD': {'code': '344', 'name': 'Гонконгский доллар', 'multiplier': 1000},
        'DKK': {'code': '208', 'name': 'Датская крона', 'multiplier': 100},
        'USD': {'code': '840', 'name': 'Доллар США', 'multiplier': 100},
        'EUR': {'code': '978', 'name': 'Евро', 'multiplier': 100},
        'INR': {'code': '356', 'name': 'Индийская рупия', 'multiplier': 10000},
        'KZT': {'code': '398', 'name': 'Тенге', 'multiplier': 10000},
        'CAD': {'code': '124', 'name': 'Канадский доллар', 'multiplier': 100},
        'KGS': {'code': '417', 'name': 'Сом', 'multiplier': 10000},
        'CNY': {'code': '156', 'name': 'Юань', 'multiplier': 100},
        'MDL': {'code': '498', 'name': 'Молдавский лей', 'multiplier': 1000},
        'NOK': {'code': '578', 'name': 'Норвежская крона', 'multiplier': 1000},
        'PLN': {'code': '985', 'name': 'Злотый', 'multiplier': 100},
        'RON': {'code': '946', 'name': 'Румынский лей', 'multiplier': 100},
        'RUB': {'code': '648', 'name': 'Российский рубль', 'multiplier': 1},
        'SGD': {'code': '702', 'name': 'Сингапурский доллар', 'multiplier': 100},
        'TJS': {'code': '972', 'name': 'Сомони', 'multiplier': 1000},
        'TRY': {'code': '949', 'name': 'Турецкая лира', 'multiplier': 100},
        'TMT': {'code': '934', 'name': 'Новый туркменский манат', 'multiplier': 100},
        'UZS': {'code': '860', 'name': 'Узбекский сум', 'multiplier': 1000000},
        'UAH': {'code': '980', 'name': 'Гривна', 'multiplier': 1000},
        'CZK': {'code': '203', 'name': 'Чешская крона', 'multiplier': 1000},
        'SEK': {'code': '752', 'name': 'Шведская крона', 'multiplier': 1000},
        'CHF': {'code': '756', 'name': 'Швейцарский франк', 'multiplier': 100},
        'ZAR': {'code': '710', 'name': 'Рэнд', 'multiplier': 1000},
        'KRW': {'code': '410', 'name': 'Вона', 'multiplier': 100000},
        'JPY': {'code': '392', 'name': 'Иена', 'multiplier': 10000}
    }

    def __init__(self, year, broker_as_income=False, only_dividends=False):
        template_path = JalSettings.path(JalSettings.PATH_TEMPLATES) + Setup.NDFL3_TEMPLATE_PATH
        template_file = template_path + os.sep + f"{year}.json"
        try:
            with open(template_file, 'r', encoding='utf-8') as json_template:
                self._tax_form = json.load(json_template)
        except FileNotFoundError:
            raise ValueError(self.tr("3-NDFL template not found for given year: ") + f"{year}")
        self._only_dividends = only_dividends
        self._broker_as_income = broker_as_income
        self._year = year
        self.currency = None
        self.broker_name = ''
        self.broker_iso_country = "000"
        self.stored_data = {
            "Дивиденды": {"dividend": self.append_dividend},
            "Акции": {"trade": self.append_stock_trade},
            "Облигации": {"bond_trade": self.append_stock_trade, "bond_interest": self.append_bond_interest},
            "ПФИ": {"trade": self.append_derivative_trade},
            "Проценты": {"interest": self.append_other_income}
        }

    def tr(self, text):
        return QApplication.translate("NDFL3", text)

    # takes tax_report, generated by TaxesRus.prepare_tax_report(), and puts its data into tax form
    # Parameters contain auxiliary params of report
    def update_taxes(self, tax_report, parameters):
        try:
            self.currency = self.currencies[parameters['currency']]
        except KeyError:
            logging.error(self.tr("Currency is not supported for 3-NDFL: ") + parameters['currency'])
            return
        self.broker_name = parameters['broker_name']
        self.broker_iso_country = parameters['broker_iso_country']
        for section in tax_report:
            if section in self.stored_data:
                for template in self.stored_data[section]:
                    for item in tax_report[section]:
                        if item['report_template'] == template:
                            self.stored_data[section][template](item)

    # Save tax form in file format of russian tax software "Декларация 2.0" with given filename
    def save(self, filename):
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(self._tax_form, f, indent=2, ensure_ascii=False)

    def append_dividend(self, dividend):
        if self._broker_as_income:
            income_source = self.broker_name
            income_iso_country = self.broker_iso_country
            if income_iso_country == '000':
                logging.error(self.tr("Account country is not set for asset, dividend isn't exported into 3-NDFL ") + f"'{income_source}'")
                return
        else:
            income_source = f"Дивиденд от {dividend['symbol']} ({dividend['full_name']})"
            income_iso_country = dividend["country_iso"]
            if income_iso_country == '000':
                logging.error(self.tr("Country is not set for asset, dividend isn't exported into 3-NDFL ") + f"'{income_source}'")
                return
        dividend_record = {
          "SourseName": income_source,
          "CountryCode1": income_iso_country,
          "CountryCode2": self.broker_iso_country,
          "IncomeGetDate": datetime.fromtimestamp(dividend['payment_date'], tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%S"),
          "HoldDate": datetime.fromtimestamp(dividend['payment_date'], tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%S"),
          "CurrencyCode": self.currency['code'],
          "CurrencyName": self.currency['name'],
          "CurrencyExchange": float(dividend['rate'] * self.currency['multiplier']),
          "CurrencyUnits": self.currency['multiplier'],
          "CurrencyExchangeHoldDate": float(dividend['rate'] * self.currency['multiplier']),
          "CurrencyUnitsHoldDate": self.currency['multiplier'],
          "IncomeRoubleSum": float(dividend['amount_rub']),
          "TaxCurrencySum": float(dividend['tax']),
          "TaxRoubleSum": float(dividend['tax_rub']),
          "TaxRate": 0,
          "KIKIncomeType": 0,
          "KIKNum": "",
          "TaxKIKHoldRF": 0.0,
          "IncomeCod": "1010",   # Dividend code
          "IncomeSum": float(dividend['amount']),
          "SubtractCod": "",
          "SubtractSum": 0.0
        }
        self._tax_form[FOREIGN_SECTION].append(dividend_record)

    def append_stock_trade(self, trade):
        if trade['c_qty'] < 0:  # short position - swap close/open dates/rates
            trade['cs_date'] = trade['os_date']
            trade['cs_rate'] = trade['os_rate']
        if self._broker_as_income:
            income_source = self.broker_name
        else:
            income_source = f"Доход от сделки с {trade['c_symbol']} ({trade['c_isin']})"
        trade_record = {
            "SourseName": income_source,
            "CountryCode1": self.broker_iso_country,
            "CountryCode2": self.broker_iso_country,
            "IncomeGetDate": datetime.fromtimestamp(trade['cs_date'], tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%S"),
            "HoldDate": datetime.fromtimestamp(trade['cs_date'], tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%S"),
            "CurrencyCode": self.currency['code'],
            "CurrencyName": self.currency['name'],
            "CurrencyExchange": float(trade['cs_rate'] * self.currency['multiplier']),
            "CurrencyUnits": self.currency['multiplier'],
            "CurrencyExchangeHoldDate": float(trade['cs_rate'] * self.currency['multiplier']),
            "CurrencyUnitsHoldDate": self.currency['multiplier'],
            "IncomeRoubleSum": float(trade['income_rub']),
            "TaxCurrencySum": 0.0,
            "TaxRoubleSum": 0.0,
            "TaxRate": 0,
            "KIKIncomeType": 0,
            "KIKNum": "",
            "TaxKIKHoldRF": 0.0,
            "IncomeCod": "1530",  # '(01)Доходы от реализации ЦБ (обращ-ся на орг. рынке ЦБ)'
            "IncomeSum": float(trade['income']),
            "SubtractCod": "201",
            "SubtractSum": float(trade['spending_rub'])
        }
        self._tax_form[FOREIGN_SECTION].append(trade_record)

    def append_derivative_trade(self, trade):
        if trade['c_qty'] < 0:  # short position - swap close/open dates/rates
            trade['cs_date'] = trade['os_date']
            trade['cs_rate'] = trade['os_rate']
        if self._broker_as_income:
            income_source = self.broker_name
        else:
            income_source = f"Доход от сделки с {trade['c_symbol']} ({trade['c_isin']})"
        trade_record = {
            "SourseName": income_source,
            "CountryCode1": self.broker_iso_country,
            "CountryCode2": self.broker_iso_country,
            "IncomeGetDate": datetime.fromtimestamp(trade['cs_date'], tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%S"),
            "HoldDate": datetime.fromtimestamp(trade['cs_date'], tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%S"),
            "CurrencyCode": self.currency['code'],
            "CurrencyName": self.currency['name'],
            "CurrencyExchange": float(trade['cs_rate'] * self.currency['multiplier']),
            "CurrencyUnits": self.currency['multiplier'],
            "CurrencyExchangeHoldDate": float(trade['cs_rate'] * self.currency['multiplier']),
            "CurrencyUnitsHoldDate": self.currency['multiplier'],
            "IncomeRoubleSum": float(trade['income_rub']),
            "TaxCurrencySum": 0.0,
            "TaxRoubleSum": 0.0,
            "TaxRate": 0,
            "KIKIncomeType": 0,
            "KIKNum": "",
            "TaxKIKHoldRF": 0.0,
            "IncomeCod": "1532",  # '(06)Доходы по оп-циям с ПФИ (обращ-ся на орг. рынке ЦБ), баз. ак. по которым являются ЦБ'
            "IncomeSum": float(trade['income']),
            "SubtractCod": "206",
            "SubtractSum": float(trade['spending_rub'])
        }
        self._tax_form[FOREIGN_SECTION].append(trade_record)

    def append_bond_interest(self, interest):
        if self._broker_as_income:
            income_source = self.broker_name
        else:
            income_source = f"Купонный доход от {interest['symbol']} ({interest['isin']})"
        interest_record = {
            "SourseName": income_source,
            "CountryCode1": self.broker_iso_country,
            "CountryCode2": self.broker_iso_country,
            "IncomeGetDate": datetime.fromtimestamp(interest['o_date'], tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%S"),
            "HoldDate": datetime.fromtimestamp(interest['o_date'], tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%S"),
            "CurrencyCode": self.currency['code'],
            "CurrencyName": self.currency['name'],
            "CurrencyExchange": float(interest['rate'] * self.currency['multiplier']),
            "CurrencyUnits": self.currency['multiplier'],
            "CurrencyExchangeHoldDate": float(interest['rate'] * self.currency['multiplier']),
            "CurrencyUnitsHoldDate": self.currency['multiplier'],
            "IncomeRoubleSum": float(interest['interest_rub']),
            "TaxCurrencySum": 0.0,
            "TaxRoubleSum": 0.0,
            "TaxRate": 0,
            "KIKIncomeType": 0,
            "KIKNum": "",
            "TaxKIKHoldRF": 0.0,
            "IncomeCod": "1530",  # '(01)Доходы от реализации ЦБ (обращ-ся на орг. рынке ЦБ)'
            "IncomeSum": float(interest['interest']),
            "SubtractCod": "",
            "SubtractSum": 0.0
        }
        self._tax_form[FOREIGN_SECTION].append(interest_record)

    def append_other_income(self, payment):
        payment_record = {
          "SourseName": self.broker_name,
          "CountryCode1": self.broker_iso_country,
          "CountryCode2": self.broker_iso_country,
          "IncomeGetDate": datetime.fromtimestamp(payment['payment_date'], tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%S"),
          "HoldDate": datetime.fromtimestamp(payment['payment_date'], tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%S"),
          "CurrencyCode": self.currency['code'],
          "CurrencyName": self.currency['name'],
          "CurrencyExchange": float(payment['rate'] * self.currency['multiplier']),
          "CurrencyUnits": self.currency['multiplier'],
          "CurrencyExchangeHoldDate": float(payment['rate'] * self.currency['multiplier']),
          "CurrencyUnitsHoldDate": self.currency['multiplier'],
          "IncomeRoubleSum": float(payment['amount_rub']),
          "TaxCurrencySum": 0.0,
          "TaxRoubleSum": 0.0,
          "TaxRate": 0,
          "KIKIncomeType": 0,
          "KIKNum": "",
          "TaxKIKHoldRF": 0.0,
          "IncomeCod": "4800",   # 'Иные доходы'
          "IncomeSum": float(payment['amount']),
          "SubtractCod": "",
          "SubtractSum": 0.0
        }
        self._tax_form[FOREIGN_SECTION].append(payment_record)
