<?php

namespace Garradin\Plugin\Facturation;

use DateTime;
use Garradin\DB;
use Garradin\UserException;
use Garradin\Services\Services_User;

class Facture
{
	private $keys = [
		'type_facture', // 0 : devis, 1 : facture, 2 : reçu cerfa, 3 : reçu cotis
		'numero',
		'receveur_membre',
		'receveur_id',
		'date_emission', // Reçus : date du don
		'date_echeance', // Reçus : date d'édition du reçu
		'reglee',
		'archivee',
		'moyen_paiement',
		'contenu',
		'total'
	];

	public $types = [
			DEVIS => [
				'id' => DEVIS,
				'accounts' => [],
				'label' => 'Devis',
				'help' => ''],
			FACT => [
				'id' => FACT,
				'accounts' => [],
				'label' => 'Facture',
				'help' => ''],
			CERFA => [
				'id' => CERFA,
				'accounts' => [],
				'label' => 'Reçu fiscal',
				'help' => 'Reçu fiscal pour un don (membre ou client)'],
			COTIS => [
				'id' => COTIS,
				'accounts' => [],
				'label' => 'Reçu de cotisation',
				'help' => 'Reçu pour une cotisation payée par un·e membre'],
		];

	public function __construct()
	{

	}

	// Fix : est dépendant de l'ordre des données dans l'array
	// et implique que toutes les données soient présentes (pas possible de faire un update partiel)
	public function _checkFields(&$datas)
	{
		foreach($datas as $k=>$data)
		{
			if (!in_array($k, $this->keys))
			{
				throw new UserException("Clé inattendue : $k.");
			}

			if(!is_array($data)){
				$datas[$k] = trim($data);
			}
			if ($datas[$k] === '')
			{
				throw new UserException("La valeur de $k est vide");
			}

			switch($k)
			{
				case 'type_facture':
					if (!array_key_exists($datas[$k], $this->types)) {
						throw new UserException("$k est de type non-attendue ($data).");
					}
					if ($datas[$k] < 2) {
						$fac = true;
						$cerfa = false;
						$recu = false;
					}
					elseif ($datas[$k] == 2) {
						$fac = false;
						$cerfa = true;
						$recu = false;
					}
					elseif ($datas[$k] == 3) {
						$fac = false;
						$cerfa = false;
						$recu = true;
					}
					break;
				case 'receveur_membre':
				case 'reglee':
				case 'archivee':
					if ($datas[$k] != 1 && $datas[$k] != 0) {
						throw new UserException("$k est de valeur non-attendue ($data).");
					}
					break;
				case 'receveur_id':
					if (!is_numeric($datas[$k]) || $datas[$k] < 0) {
						throw new UserException("L'id du receveur est non-attendu ($data).");
					}
					break;
				case 'date_emission':
					$datas[$k] = \DateTime::createFromFormat('!d/m/Y', $data)->format('Y-m-d');
					break;
				case 'date_echeance':
					$datas[$k] = \DateTime::createFromFormat('!d/m/Y', $data)->format('Y-m-d');
					if (DateTime::createFromFormat('!Y-m-d', $datas[$k])->format('U') < DateTime::createFromFormat('!Y-m-d', $datas['date_emission'])->format('U'))
					{
						throw new UserException("La date d'échéance est antérieure à la date d'émission ($data).");
					}
					break;
				case 'moyen_paiement':
					if (!array_key_exists($datas[$k], $this->listMoyensPaiement())) {
						throw new UserException("Le moyen de paiement ne correspond pas à la liste interne ($data).");
					}
					break;
				case 'contenu':
					if ($fac)
					{
						if (!is_array($datas[$k]) || empty($datas[$k])) {
							throw new UserException("Le contenu du document est vide ($data).");
						}
						$total = 0;
						foreach($datas[$k] as $g => $r)
						{
							if (empty($r['designation']) && empty($r['prix']))
							{
								unset($datas[$k][$g]);
								unset($datas[$k]['prix']);
								continue;
							}
							elseif (empty($r['prix']))
							{
								$datas[$k]['prix'] = 0;
							}
							
							if (!is_int($r['prix']))
							{
								throw new UserException('Un (ou plus) des prix n\'est pas un entier.');
							}

							$total += $r['prix']; 
						}

						if($fac && !$total)
						{
							throw new UserException("Toutes les désignations/prix sont vides.");
						}
					}
					elseif ($cerfa)
					{

					}
					elseif ($recu)
					{
						// $fields = ['id', 'intitule', 'date', 'expiration'];
						// foreach ($datas[$k]as $)
					}
					$datas[$k] = json_encode($datas[$k]);
					break;
				case 'total':
					if ($cerfa && $datas[$k] < 1) {
						throw new UserException('Le total ne peut être inférieur à 1€ pour les reçus (bug encore non résolu).');
					}
					if ($fac && !isset($datas['contenu'])) {
						throw new UserException("Pas de contenu fourni pour vérifier le total.");
					}
					if ($fac && $total != $datas[$k])
					{
						throw new UserException("Les totaux sont différents ($total != $datas[$k].");
					}
					break;
			}
		}
	}

	public function add($data)
	{
		$db = DB::getInstance();

		$this->_checkFields($data);

		if(isset($data['numero']) && $db->test('plugin_facturation_factures', 'numero = ? COLLATE NOCASE', $data['numero']))
		{
			throw new UserException('Un document avec ce numéro existe déjà, hors le numéro doit être unique.');
		}
		$db->insert('plugin_facturation_factures', $data);
		return $db->lastInsertRowId();		
	}

	public function get($id)
	{
		$db = DB::getInstance();

		$r = $db->first('SELECT * FROM plugin_facturation_factures WHERE id = ? LIMIT 1;', (int)$id);

		if(!$r)
		{	
			throw new UserException("Pas de document retournée avec cet id.");
		}

		if ($r->contenu)
		{
			$r->contenu = json_decode($r->contenu, true);
		}

		$r->date_emission = \DateTime::createFromFormat('!Y-m-d', $r->date_emission);
		if ($r->date_echeance)
		{
			$r->date_echeance= \DateTime::createFromFormat('!Y-m-d', $r->date_echeance);
		}

		return $r;
	}

	public function listAll()
	{
		$r = (array)DB::getInstance()->get('SELECT *, strftime(\'%s\', date_emission) AS date_emission,
			strftime(\'%s\', date_echeance) AS date_echeance
			FROM plugin_facturation_factures');

		foreach ($r as $e)
		{
			if($e->contenu)
			{
				$e->contenu = json_decode((string)$e->contenu, true);
			}
		}

		return $r;
	}

	public function edit($id, $data = [])
	{
		$db = DB::getInstance();

		$this->_checkFields($data);

		if(isset($data['numero']) && $db->test('plugin_facturation_factures', 'numero = ? COLLATE NOCASE AND id != ?', $data['numero'], (int)$id))
		{
			throw new UserException('Un document avec ce numéro existe déjà, hors le numéro doit être unique.');
		}
		return $db->update('plugin_facturation_factures', $data, $db->where('id', (int)$id));
	}

	public function listUserDoc($base, $id)
	{
		$client = new Client;

		if ($base == 0) // Si c'est un client
		{
			if(!$client->get($id))
			{
				throw new UserException("Ce client n'existe pas.");
			}
		}
		else // Si c'est un membre de l'asso
		{
			throw new UserException("Woopsie, g pô encore implémenté l'usage des membres de l'asso comme clients");
		}

		$r = (array)DB::getInstance()->get('SELECT *, strftime(\'%s\', date_emission) AS date_emission,
			strftime(\'%s\', date_echeance) AS date_echeance
			FROM plugin_facturation_factures
			WHERE receveur_membre = ? AND receveur_id = ?', (int)$base, (int)$id);

		foreach ($r as $e)
		{
			if ($e->contenu)
			{
				$e->contenu = json_decode((string)$e->contenu, true);
			}
		}

		return empty($r)?false:$r;
	}

	public function hasDocs($base, $id)
	{
		$client = new Client;

		if ($base == 0) // Si c'est un client
		{
			if(!$client->get($id))
			{
				throw new UserException("Ce client n'existe pas.");
			}
		}
		else // Si c'est un membre de l'asso
		{
			throw new UserException("Woopsie, g pô encore implémenté l'usage des membres de l'asso comme clients");
		}

		return DB::getInstance()->test('plugin_facturation_factures', 'receveur_membre = '. $base .' AND receveur_id = '. $id);
	}

	// ** Pour type reçu **

	public $recu_fields = ['id', 'label', 'amount', 'date', 'expiry', 'paid', 'paid_amount'];

	public function getCotis(int $user_id, int $su_id = null)
	{
		$where = 'WHERE su.id_user = ?';
		if (null !== $su_id)
		{
			$where .= ' AND su.id = '.$su_id;
		}

		$sql = 'SELECT su.id, s.label, su.date, MAX(su.expiry_date) as expiry, sf.label as fee, sf.amount as amount, su.paid, SUM(tl.debit) as paid_amount
			FROM services_users su
			INNER JOIN services s ON s.id = su.id_service
			LEFT JOIN services_fees sf ON sf.id = su.id_fee
			LEFT JOIN acc_transactions_users tu ON tu.id_service_user = su.id
			LEFT JOIN acc_transactions_lines tl ON tl.id_transaction = tu.id_transaction
			'.$where.'
			GROUP BY su.id
			ORDER BY su.date;';

		return DB::getInstance()->get($sql, $user_id);
	}

    public function listMoyensPaiement($assoc = false)
    {
        $db = DB::getInstance();

        $query = 'SELECT code, nom FROM plugin_facturation_paiement ORDER BY nom COLLATE NOCASE;';

        if ($assoc) {
            return $db->getAssoc($query);
        }
        else {
            return $db->getGrouped($query);
        }
	}
	
    public function getMoyenPaiement($code)
    {
        $db = DB::getInstance();
        return $db->firstColumn('SELECT nom FROM plugin_facturation_paiement WHERE code = ?;', $code);
    }
	
	public function delete($id)
	{
		return DB::getInstance()->delete('plugin_facturation_factures', 'id = '. (int)$id);
	}

}