<?php
namespace API\ChorusPro;

use API\Partenaire\IFactDematReceiver;
use API\Partenaire\IPartenaire;
use DateTime;
use DateTimeInterface;
use Entity\Model\CaEntet;
use Entity\Model\Enum\EFacDematFlowStatus;
use Entity\Model\Enum\EFacDematStatusReason;
use Entity\Model\FacDematFlow;
use Entity\Model\Fourniss;
use Entity\Model\Modepaie;
use Entity\Model\Paravrac;
use Entity\Model\Societe;
use Helpers\Session\ParseBase1;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\StreamInterface;
use Psr\SimpleCache\CacheInterface;

class ChorusPartenaire implements IPartenaire, IFactDematReceiver
{

    private ContainerInterface $container;
    private int $id;
    private string $label;
    private ChorusWs $ws;

//_____________________________________________________________________________
// Constructor

    public function __construct(
        ContainerInterface $container,
        int $id,
        string $label,
        string $client,
        string $clientSecret,
        string $chorusLogin,
        string $chorusPassword
    ) {
        $this->container = $container;
        $this->id = $id;
        $this->label = $label;
        $this->ws = new ChorusWs(
            $id,
            $container->get(Paravrac::class)->isEnvProd() && ! str_ends_with($label, ' test'),
            $client,
            $clientSecret,
            $chorusLogin,
            $chorusPassword,
            $this->container->get(CacheInterface::class)
        );
    }
    
    /**
     * @param array<mixed> $config As given by FactureDematRepo
     */
    public static function getFromConfig(ContainerInterface $container, array $config) :self
    {
        return new ChorusPartenaire(
            $container,
            $config['ID'],
            $config['LABEL'],
            $config['CLIENT_ID'],
            $config['CLIENT_SECRET'],
            $config['CHORUS_LOGIN'],
            $config['CHORUS_PWD'],
        );
    }

//_____________________________________________________________________________
// Accessor

    public function getId() :int
    {
        return $this->id;
    }

    public function getLabel(bool $short = false): string
    {
        return $this->label;
    }
    
    public function getCode(): string
    {
        return 'CHORUS_'.$this->id;
    }

    public function getWs() :ChorusWs
    {
        return $this->ws;
    }

//_____________________________________________________________________________
// IFactDematReceiver

    public function getPPFId(): string
    {
        return 'NOT_USED';
    }

    public function listNext(array &$context) :array
    {
        $context['nextPage'] = $context['nextPage'] ?? 1;

        $list = $this->getWs()->listFacture($context['dateLimit'], $context['nextPage']);
        
        $context['nextPage']++;

        // Convert to Flow
        return array_map(
            fn($facture) => (object) [
                'flowId' => (string)$facture->idFacture,
                'flowType' => 'SupplierInvoice',
                'flowProfile' => 'Basic',
                'flowSyntax' => 'ChorusFacture',
                'flowDirection' => 'In',
                'submittedAt' => (new \DateTime($facture->dateDepot))->format(DateTimeInterface::RFC3339),
                'updatedAt' => (new \DateTime($facture->dateDepot))->format(DateTimeInterface::RFC3339),
            ],
            $list
        );
    }

    public function getFlowFile(string $id): StreamInterface
    {
        // TODO : improve IFactDematReceiver
        throw new \Exception('Not applicable');
    }

    public function getCdarFile(string $id): StreamInterface
    {
        // TODO : improve IFactDematReceiver
        throw new \Exception('Not applicable');
    }
    
    public function getReadableViewFile(object $flow): ?StreamInterface
    {
        return $this->getWs()->getLisible((int) $flow->flowId);
    }
    
    public function updateStatus(string $flowId, array $cdar) :void
    {
        // Chorus only handle LITIGE & REFUS
        $status = match ($cdar['status']['code']) {
            EFacDematFlowStatus::Litige->value => 'SUSPENDUE',
            EFacDematFlowStatus::Refuse->value => 'REJETEE',
            EFacDematFlowStatus::PaieTrans->value => 'MISE_EN_PAIEMENT',
            default => null,
        };
        if (! $status) {
            return;
        }
        $this->getWs()->changeStatus(
            (int) $flowId,
            $status,
            ($cdar['status']['reason'] ?? EFacDematStatusReason::Autre)->getLabel()
        );
    }

    /**
     * @return array<mixed>
     */
    public function getMappageChorus(string $id, \PDO $pdo) :array
    {
        $facture = $this->getWs()->getFacture((int) $id);
        $qbFourniss = new Fourniss($pdo);
        return [
            'TYPE_NEW' => match ($facture->references->typeFacture) {
                'FACTURE' => 'F',
                'AVOIR' => 'A',
                'ACOMPTE' => 'C',
                default => 'F',
            },
            'FACT_FOU' => $facture->numeroFacture ?? '',
            'DATE_FAFO' => $this->dateConvert($facture->references->dateFacture ?? ''),
            'DTAE_ECH' => $this->dateConvert($facture->references->dateEcheancePaiement ?? ''),
            'FabNote' => $facture->commentaire ?? '',

            'DATE_ENR' => date('Ymd'),
            'DATE_PER' => date('Ymd'),

            'IBANPAYS' => $facture->fournisseur->coordBancairesFournisseurCodePays ?? '',
            'IBANCLE' => $facture->fournisseur->coordBancairesFournisseurCleIban ?? '',
            'IBANCPT' => $facture->fournisseur->coordBancairesFournisseurCompteBancaire ?? '',

            'CODE_BANQ' => $facture->fournisseur->coordBancairesFournisseurCodeBanque ?? '',
            'CODE_GUIC' => $facture->fournisseur->coordBancairesCodeGuichet ?? '',
            'NUM_COMPT' => $facture->fournisseur->coordBancairesFournisseurCompteBancaire ?? '',
            'CLE_RIB' => $facture->fournisseur->coordBancairesFournisseurCleRib ?? '',
            'DOM_BANQ' => '',
            'CODEBIC' => $facture->fournisseur->coordBancairesBicSwift ?? '',

            'CODE_FOUR' => match ($facture->fournisseur->typeIdentifiantFournisseur) {
                'SIRET' => $qbFourniss->find([
                    'CODE_PAYS' => 'FR',
                    'NO_SIRET' => $facture->fournisseur->codeFournisseur
                ])['CODE'] ?? '',
                'UE_HORS_FRANCE' => $qbFourniss->find([
                    'NO_TVA_IC' => $facture->fournisseur->codeFournisseur
                ])['CODE'] ?? '',
                default => ''
            },
            'CODE_STE' => (new Societe($pdo))->find([
                'SIRET' => $facture->destinataire->codeDestinataire,
            ])['CODE_STE'] ?? '',
            'DEVISE' => $facture->references->codeDeviseFacture ?? '',
            'MODE_REG' => (new Modepaie($pdo))->find([
                'CODE_UBL' => match ($facture->references->modePaiement) {
                    'CHEQUE' => 20,
                    'VIREMENT' => 30 /* Credit transfer */,
                    'PRELEVEMENT' =>  31 /* Debit transfer */,
                    'ESPECE' => 10 /* In cash */,
                    'AUTRE' => 1 /* Instrument not defined */,
                    'REPORT' => 1 /* Instrument not defined */,
                    default => 1
                }
            ])['CODE_MDP'] ?? '',
            'CdeLad' => (new CaEntet($pdo))->find([
                'REFIMPCDE' => $facture->references->numeroBonCommande ?? '',
            ])['NO_CDE'] ?? '',

            'TOTAL_HT' => $facture->montantTotal->montantHtTotal ?? 0,
            'TOTAL_TTC' => $facture->montantTotal->montantTtcTotal ?? 0,

            'FouRs' => $facture->fournisseur->designationFournisseur,
            'SocRs' => $facture->destinataire->libelleDestinataire,
            'TotalTva' => $facture->montantTotal->montantTVA ?? 0,
            
            'SocEndpointScheme' => '',
            'SocEndpoint' => '',
            'FouEndpointScheme' => '',
            'FouEndpoint' => '',
            'InvoiceTypeCode' => '',
            
            'PrepaidAmount' => '',
        ];
    }

    /**
     * Convert standard date into Sql date (handle empty)
     */
    private function dateConvert(string $date) :string
    {
        return ! empty($date)
            ? (new \DateTime($date))->format('Ymd')
            : '00000000';
    }
}
