<?php
/*
 * OpenSTAManager: il software gestionale open source per l'assistenza tecnica e la fatturazione
 * Copyright (C) DevCode s.r.l.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */

namespace Modules\Interventi\Components;

use Common\SimpleModelTrait;
use DateTime;
use Illuminate\Database\Eloquent\Model;
use InvalidArgumentException;
use Modules\Anagrafiche\Anagrafica;
use Modules\Interventi\Intervento;
/*
 * Notazione: i costi sono rivolti all'azienda, i prezzi al cliente.
 *
 * @since 2.4.9
 */
use Modules\Iva\Aliquota;
use Modules\TipiIntervento\Tipo as TipoSessione;

class Sessione extends Model
{
    use SimpleModelTrait;
    use RelationTrait;

    protected $table = 'in_interventi_tecnici';

    protected $aliquota_iva = null;

    /**
     * Crea un nuova sessione collegata ad un intervento.
     *
     * @param string $inizio
     * @param string $fine
     *
     * @return self
     */
    public static function build(Intervento $intervento, Anagrafica $anagrafica, $inizio, $fine)
    {
        if (!$anagrafica->isTipo('Tecnico')) {
            throw new InvalidArgumentException('Anagrafica di tipo diverso da Tecnico');
        }

        $model = new static();

        $model->document()->associate($intervento);
        $model->anagrafica()->associate($anagrafica);

        $id_tipo = $intervento['idtipointervento'];
        $tipo_sessione = TipoSessione::find($id_tipo);
        $model->tipo()->associate($tipo_sessione);

        if (empty($fine)) {
            $fine = date_modify(date_create($inizio), '+'.($tipo_sessione->tempo_standard*60).' minutes')->format('Y-m-d H:i');
        }

        $model->orario_inizio = $inizio;
        $model->orario_fine = $fine;

        if ($tipo_sessione->calcola_km) {
            // Sede secondaria
            if (!empty($intervento['idsede_destinazione'])) {
                $sede = database()->fetchOne('SELECT km FROM an_sedi WHERE id = '.prepare($intervento['idsede_destinazione']));
                $km = $sede['km'];
            }

            // Sede legale dell'anagrafica
            else {
                $km = $intervento->anagrafica->sedeLegale->km;
            }
        }

        $model->km = empty($km) ? 0 : $km;

        $model->save();

        $model->setTipo($id_tipo, true);
        $model->save();

        return $model;
    }

    public function setTipo($id_tipo, $reset = false)
    {
        $previous = $this->idtipointervento;

        $tipo_sessione = TipoSessione::find($id_tipo);
        $this->tipo()->associate($tipo_sessione);

        if ($previous != $id_tipo || $reset) {
            $tariffa = $this->getTariffa($id_tipo);

            // Azzeramento forzato del diritto di chiamata nel caso la sessione non sia la prima dell'intervento nel giorno di inizio o fine
            $sessioni = database()->fetchArray('SELECT id FROM in_interventi_tecnici WHERE (DATE(orario_inizio) = DATE('.prepare($this->orario_inizio).') OR DATE(orario_fine) = DATE('.prepare($this->orario_fine).')) AND (prezzo_dirittochiamata != 0 OR prezzo_dirittochiamata_tecnico != 0) AND id != '.prepare($this->id).' AND idintervento = '.prepare($this->intervento->id));
            if (!empty($sessioni)) {
                $tariffa['costo_dirittochiamata_tecnico'] = 0;
                $tariffa['costo_dirittochiamata'] = 0;

                // Fix se reset non attivo
                $this->prezzo_dirittochiamata = $tariffa['costo_dirittochiamata'];
            }

            // Modifica dei costi
            $this->prezzo_ore_unitario_tecnico = $tariffa['costo_ore_tecnico'];
            $this->prezzo_km_unitario_tecnico = $tariffa['costo_km_tecnico'];
            $this->prezzo_dirittochiamata_tecnico = $tariffa['costo_dirittochiamata_tecnico'];

            // Modifica dei prezzi
            if ($reset) {
                $this->prezzo_ore_unitario = $tariffa['costo_ore'];
                $this->prezzo_km_unitario = $tariffa['costo_km'];
                $this->prezzo_dirittochiamata = $tariffa['costo_dirittochiamata'];
            }
        }
    }

    public function getOreCalcolateAttribute()
    {
        $inizio = new DateTime($this->orario_inizio);
        $diff = $inizio->diff(new DateTime($this->orario_fine));

        $ore = $diff->i / 60 + $diff->h + ($diff->days * 24);

        return $ore;
    }

    /**
     * Salva la sessione, impostando i campi dipendenti dai singoli parametri.
     *
     * @return bool
     */
    public function save(array $options = [])
    {
        $this->attributes['ore'] = $this->ore_calcolate;

        $this->attributes['prezzo_ore_consuntivo'] = $this->prezzo_manodopera + $this->prezzo_diritto_chiamata;
        $this->attributes['prezzo_km_consuntivo'] = $this->prezzo_viaggio;

        $this->attributes['prezzo_ore_consuntivo_tecnico'] = $this->costo_manodopera + $this->costo_diritto_chiamata;
        $this->attributes['prezzo_km_consuntivo_tecnico'] = $this->costo_viaggio;

        $this->attributes['sconto'] = $this->sconto_totale_manodopera;
        $this->attributes['scontokm'] = $this->sconto_totale_viaggio;

        return parent::save($options);
    }

    public function getDocumentID()
    {
        return 'idintervento';
    }

    // Relazioni Eloquent

    public function anagrafica()
    {
        return $this->belongsTo(Anagrafica::class, 'idtecnico');
    }

    public function tipo()
    {
        return $this->belongsTo(TipoSessione::class, 'idtipointervento');
    }

    public function parent()
    {
        return $this->belongsTo(Intervento::class, $this->getDocumentID());
    }

    // Costi per l'azienda

    /**
     * Restituisce il costo orario (per l'azienda) per la sessione del tecnico.
     *
     * @return float
     */
    public function getCostoOrarioAttribute()
    {
        return $this->attributes['prezzo_ore_unitario_tecnico'];
    }

    /**
     * Restituisce il costo del diritto di chiamata (per l'azienda) per la sessione del tecnico.
     *
     * @return float
     */
    public function getCostoDirittoChiamataAttribute()
    {
        return $this->attributes['prezzo_dirittochiamata_tecnico'];
    }

    /**
     * Restituisce il costo chilometrico (per l'azienda) del viaggio del tecnico.
     *
     * @return float
     */
    public function getCostoChilometricoAttribute()
    {
        return $this->attributes['prezzo_km_unitario_tecnico'];
    }

    /**
     * Restituisce il costo totale della manodopera escluso il diritto di chiamata (per l'azienda) per la sessione del tecnico.
     *
     * @return float
     */
    public function getCostoManodoperaAttribute()
    {
        return $this->costo_orario * $this->ore;
    }

    /**
     * Restituisce il costo totale (per l'azienda) del viaggio del tecnico.
     *
     * @return float
     */
    public function getCostoViaggioAttribute()
    {
        return $this->costo_chilometrico * $this->km;
    }

    // Prezzi per il cliente

    /**
     * Restituisce il prezzo del diritto di chiamata (per il cliente) per la sessione del tecnico.
     *
     * @return float
     */
    public function getPrezzoDirittoChiamataAttribute()
    {
        return $this->attributes['prezzo_dirittochiamata'];
    }

    /**
     * Restituisce il prezzo del diritto di chiamata (per il cliente) per la sessione del tecnico.
     *
     * @return float
     */
    public function getPrezzoChilometricoAttribute()
    {
        return $this->attributes['prezzo_km_unitario'];
    }

    /**
     * Restituisce il prezzo del diritto di chiamata (per il cliente) per la sessione del tecnico.
     *
     * @return float
     */
    public function getPrezzoOrarioAttribute()
    {
        return $this->attributes['prezzo_ore_unitario'];
    }

    /**
     * Restituisce il prezzo totale della manodopera escluso il diritto di chiamata (per il cliente) per la sessione del tecnico.
     *
     * @return float
     */
    public function getPrezzoManodoperaAttribute()
    {
        return $this->prezzo_orario * $this->ore;
    }

    /**
     * Restituisce lo sconto totale km in euro.
     *
     * @return float
     */
    public function getScontoTotaleManodoperaAttribute()
    {
        return calcola_sconto([
            'sconto' => $this->sconto_unitario,
            'prezzo' => $this->prezzo_orario,
            'qta' => $this->ore,
            'tipo' => $this->tipo_sconto,
        ]);
    }

    /**
     * Restituisce il prezzo totale scontato (per il cliente) del viaggio del tecnico.
     *
     * @return float
     */
    public function getPrezzoManodoperaScontatoAttribute()
    {
        return $this->prezzo_manodopera - $this->sconto_totale_manodopera;
    }

    /**
     * Restituisce il prezzo totale (per il cliente) del viaggio del tecnico.
     *
     * @return float
     */
    public function getPrezzoViaggioAttribute()
    {
        return $this->prezzo_chilometrico * $this->km;
    }

    /**
     * Restituisce lo sconto totale km in euro.
     *
     * @return float
     */
    public function getScontoTotaleViaggioAttribute()
    {
        return calcola_sconto([
            'sconto' => $this->scontokm_unitario,
            'prezzo' => $this->prezzo_chilometrico,
            'qta' => $this->km,
            'tipo' => $this->tipo_scontokm,
        ]);
    }

    /**
     * Restituisce il prezzo totale scontato (per il cliente) del viaggio del tecnico.
     *
     * @return float
     */
    public function getPrezzoViaggioScontatoAttribute()
    {
        return $this->prezzo_viaggio - $this->sconto_totale_viaggio;
    }

    // Attributi di contabilità

    /**
     * Restituisce l'imponibile dell'elemento.
     *
     * @return float
     */
    public function getImponibileAttribute()
    {
        return $this->prezzo_manodopera + $this->prezzo_viaggio + $this->prezzo_diritto_chiamata;
    }

    /**
     * Restituisce il totale imponibile dell'elemento.
     *
     * @return float
     */
    public function getTotaleImponibileAttribute()
    {
        return $this->prezzo_manodopera_scontato + $this->prezzo_viaggio_scontato + $this->prezzo_diritto_chiamata;
    }

    /**
     * Restituisce il totale (imponibile + iva) dell'elemento.
     *
     * @return float
     */
    public function getTotaleAttribute()
    {
        return $this->totale_imponibile + $this->iva;
    }

    /**
     * Restituisce la spesa relativa all'elemento.
     *
     * @return float
     */
    public function getSpesaAttribute()
    {
        return $this->costo_manodopera + $this->costo_viaggio + $this->costo_diritto_chiamata;
    }

    /**
     * Restituisce il margine totale (imponibile - spesa) relativo all'elemento.
     *
     * @return float
     */
    public function getMargineAttribute()
    {
        return $this->imponibile - $this->spesa;
    }

    /**
     * Restituisce lo sconto della riga corrente in euro.
     *
     * @return float
     */
    public function getScontoAttribute()
    {
        return $this->sconto_totale_manodopera + $this->sconto_totale_viaggio;
    }

    /**
     * Restituisce il margine percentuale relativo all'elemento.
     *
     * @return float
     */
    public function getMarginePercentualeAttribute()
    {
        return (1 - ($this->spesa / ($this->totale_imponibile))) * 100;
    }

    /**
     * Restituisce il ricarico percentuale del documento.
     *
     * @return float
     */
    public function getRicaricoPercentualeAttribute()
    {
        return $this->imponibile ? (($this->imponibile / $this->spesa) - 1) * 100 : 100;
    }

    public function getIvaIndetraibileAttribute()
    {
        return $this->iva / 100 * $this->aliquota->indetraibile;
    }

    public function getIvaAttribute()
    {
        return ($this->totale_imponibile) * $this->aliquota->percentuale / 100;
    }

    public function getIvaDetraibileAttribute()
    {
        return $this->iva - $this->iva_indetraibile;
    }

    public function getAliquotaAttribute()
    {
        if (!isset($this->aliquota_iva)) {
            $id_iva = $this->intervento->anagrafica->idiva_vendite ?: setting('Iva predefinita');

            $this->aliquota_iva = Aliquota::find($id_iva);
        }

        return $this->aliquota_iva;
    }

    protected function getTariffa($id_tipo)
    {
        $database = database();

        // Costi unitari dalla tariffa del tecnico
        $result = $database->fetchOne('SELECT * FROM in_tariffe WHERE idtecnico='.prepare($this->anagrafica->id).' AND idtipointervento = '.prepare($id_tipo));

        // Costi unitari del contratto
        $id_contratto = $this->intervento->id_contratto;
        if (!empty($id_contratto)) {
            $tariffa_contratto = $database->fetchOne('SELECT costo_ore, costo_km, costo_dirittochiamata FROM co_contratti_tipiintervento WHERE idcontratto = '.prepare($id_contratto).' AND idtipointervento = '.prepare($id_tipo));

            if (!empty($tariffa_contratto)) {
                $result = array_merge($result, $tariffa_contratto);
            }
        }

        return $result;
    }
}
