# coding: utf-8
from __future__ import unicode_literals

import datetime
import zipfile
from csv import QUOTE_NONNUMERIC
from six import BytesIO

import six
import frappe
import pandas as pd
from frappe import _
from .datev_constants import DataCategory


def get_datev_csv(data, filters, csv_class):
	"""
	Fill in missing columns and return a CSV in DATEV Format.

	For automatic processing, DATEV requires the first line of the CSV file to
	hold meta data such as the length of account numbers oder the category of
	the data.

	Arguments:
	data -- array of dictionaries
	filters -- dict
	csv_class -- defines DATA_CATEGORY, FORMAT_NAME and COLUMNS
	"""
	empty_df = pd.DataFrame(columns=csv_class.COLUMNS)
	data_df = pd.DataFrame.from_records(data)
	result = empty_df.append(data_df, sort=True)

	if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS:
		result['Belegdatum'] = pd.to_datetime(result['Belegdatum'])

	if csv_class.DATA_CATEGORY == DataCategory.ACCOUNT_NAMES:
		result['Sprach-ID'] = 'de-DE'

	data = result.to_csv(
		# Reason for str(';'): https://github.com/pandas-dev/pandas/issues/6035
		sep=str(';'),
		# European decimal seperator
		decimal=',',
		# Windows "ANSI" encoding
		encoding='latin_1',
		# format date as DDMM
		date_format='%d%m',
		# Windows line terminator
		line_terminator='\r\n',
		# Do not number rows
		index=False,
		# Use all columns defined above
		columns=csv_class.COLUMNS,
		# Quote most fields, even currency values with "," separator
		quoting=QUOTE_NONNUMERIC
	)

	data = data.encode('latin_1', errors='replace')

	header = get_header(filters, csv_class)
	header = ';'.join(header).encode('latin_1', errors='replace')

	# 1st Row: Header with meta data
	# 2nd Row: Data heading (Überschrift der Nutzdaten), included in `data` here.
	# 3rd - nth Row: Data (Nutzdaten)
	return header + b'\r\n' + data


def get_header(filters, csv_class):
	description = filters.get('voucher_type', csv_class.FORMAT_NAME)
	company = filters.get('company')
	datev_settings = frappe.get_doc('DATEV Settings', {'client': company})
	default_currency = frappe.get_value('Company', company, 'default_currency')
	coa = frappe.get_value('Company', company, 'chart_of_accounts')
	coa_short_code = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '')

	header = [
		# DATEV format
		#	"DTVF" = created by DATEV software,
		#	"EXTF" = created by other software
		'"EXTF"',
		# version of the DATEV format
		#	141 = 1.41, 
		#	510 = 5.10,
		#	720 = 7.20
		'700',
		csv_class.DATA_CATEGORY,
		'"%s"' % csv_class.FORMAT_NAME,
		# Format version (regarding format name)
		csv_class.FORMAT_VERSION,
		# Generated on
		datetime.datetime.now().strftime('%Y%m%d%H%M%S') + '000',
		# Imported on -- stays empty
		'',
		# Origin. Any two symbols, will be replaced by "SV" on import.
		'"EN"',
		# I = Exported by
		'"%s"' % frappe.session.user,
		# J = Imported by -- stays empty
		'',
		# K = Tax consultant number (Beraternummer)
		datev_settings.get('consultant_number', '0000000'),
		# L = Tax client number (Mandantennummer)
		datev_settings.get('client_number', '00000'),
		# M = Start of the fiscal year (Wirtschaftsjahresbeginn)
		frappe.utils.formatdate(filters.get('fiscal_year_start'), 'yyyyMMdd'),
		# N = Length of account numbers (Sachkontenlänge)
		str(filters.get('account_number_length', 4)),
		# O = Transaction batch start date (YYYYMMDD)
		frappe.utils.formatdate(filters.get('from_date'), 'yyyyMMdd') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
		# P = Transaction batch end date (YYYYMMDD)
		frappe.utils.formatdate(filters.get('to_date'), 'yyyyMMdd') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
		# Q = Description (for example, "Sales Invoice") Max. 30 chars
		'"{}"'.format(_(description)) if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
		# R = Diktatkürzel
		'',
		# S = Buchungstyp
		#	1 = Transaction batch (Finanzbuchführung),
		#	2 = Annual financial statement (Jahresabschluss)
		'1' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
		# T = Rechnungslegungszweck
		#	0 oder leer = vom Rechnungslegungszweck unabhängig
		#	50 = Handelsrecht
		#	30 = Steuerrecht
		#	64 = IFRS
		#	40 = Kalkulatorik
		#	11 = Reserviert
		#	12 = Reserviert
		'0' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
		# U = Festschreibung
		# TODO: Filter by Accounting Period. In export for closed Accounting Period, this will be "1"
		'0',
		# V = Default currency, for example, "EUR"
		'"%s"' % default_currency if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
		# reserviert
		'',
		# Derivatskennzeichen
		'',
		# reserviert
		'',
		# reserviert
		'',
		# SKR
		'"%s"' % coa_short_code,
		# Branchen-Lösungs-ID
		'',
		# reserviert
		'',
		# reserviert
		'',
		# Anwendungsinformation (Verarbeitungskennzeichen der abgebenden Anwendung)
		''
	]
	return header


def zip_and_download(zip_filename, csv_files):
	"""
	Put CSV files in a zip archive and send that to the client.

	Params:
	zip_filename	Name of the zip file
	csv_files		list of dicts [{'file_name': 'my_file.csv', 'csv_data': 'comma,separated,values'}]
	"""
	zip_buffer = BytesIO()

	zip_file = zipfile.ZipFile(zip_buffer, mode='w', compression=zipfile.ZIP_DEFLATED)
	for csv_file in csv_files:
		zip_file.writestr(csv_file.get('file_name'), csv_file.get('csv_data'))

	zip_file.close()

	frappe.response['filecontent'] = zip_buffer.getvalue()
	frappe.response['filename'] = zip_filename
	frappe.response['type'] = 'binary'