<?php

namespace App\Exports;

use App\Models\Servidor;
use App\Models\UnidadeOrganizacional;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Spatie\Enum\Enum;

class ServidoresExport implements FromCollection, WithHeadings, WithMapping
{
    private $servidores;
    private $colunas;
    private $colunasUOR;

    public function __construct(Builder $servidoresQuery, Collection $colunas)
    {
        $this->colunas = $colunas;
        $this->colunasUOR = collect(['secretaria', 'secao', 'endereco_secao', 'telefone_secao']);
        $this->servidores = $servidoresQuery->select($this->colunasSemRelacionamento()->toArray())->get();
    }

    public function collection()
    {
        return $this->servidores;
    }

    public function map($servidor) : array
    {
        $colunas = $this->nomesColunas();

        if ($this->possuiRelacionamento()) {
            if ($colunas->contains('telefone')) {
                $servidor = $this->obtemTelefone($servidor);
            }

            if ($colunas->contains('email')) {
                $servidor = $this->obtemEmail($servidor);
            }

            if ($colunas->contains('endereco')) {
                $servidor = $this->obtemEndereco($servidor);
            }

            if ($this->possuiColunasUnidadeOrganizacional($colunas)) {
                $servidor = $this->obtemUnidadeOrganizacional($servidor, $colunas);
            }
        }

        $linha = [];

        $colunas->each(function ($coluna) use ($servidor, &$linha) {
            if ($servidor->$coluna instanceof \DateTime) {
                $linha[$coluna] = $servidor->$coluna->format('d/m/Y');
                return;
            }

            if ($servidor->$coluna instanceof Enum) {
                $linha[$coluna] = $servidor->$coluna->getValue();
                return;
            }

            $linha[$coluna] = $servidor->$coluna;
        });

        return collect($linha)->unique()->except(['id'])->toArray();
    }

    private function possuiColunasUnidadeOrganizacional($colunas)
    {
        $contemColuna = false;
        $this->colunasUOR->each(function ($coluna) use ($colunas, &$contemColuna) {
            if ($colunas->contains($coluna)) {
                $contemColuna = true;
            }
        });

        return $contemColuna;
    }

    public function headings(): array
    {
        return $this->nomesColunas()->toArray();
    }

    private function possuiRelacionamento()
    {
        return $this->nomesColunas()->intersect($this->colunasRelacionamento())->isNotEmpty();
    }

    private function obtemTelefone(Servidor $servidor)
    {
        $telefone = $servidor->contatos()->telefones()->first();
        if ($telefone) {
            $servidor->telefone = $telefone->valor;
        }

        return $servidor;
    }

    private function obtemUnidadeOrganizacional(Servidor $servidor, Collection $colunas)
    {
        $secao = optional($servidor->registros()->ativos()->first())->unidadeOrganizacional;

        if (! $secao) {
            Log::warning("Registro de servidor sem unidade organizacional", ['servidor' => $servidor->id]);
            $servidor->secretaria = '';
            return $servidor;
        }

        if ($colunas->contains('secretaria')) {
            $servidor->secretaria = $this->obtemSecretaria($secao);
        }

        if ($colunas->contains('secao')) {
            $servidor->secao = $secao->sigla_nome;
        }

        if ($colunas->contains('telefone_secao')) {
            $servidor->telefone_secao = $this->obtemTelefoneSecao($secao);
        }

        if ($colunas->contains('endereco_secao')) {
            $servidor->endereco_secao = $this->obtemEnderecoSecao($secao);
        }

        return $servidor;
    }

    private function obtemSecretaria(UnidadeOrganizacional $secao)
    {
        $secretaria = $secao->secretaria();
     
        if ($secretaria) {
            return $secretaria->sigla_nome;
        }

        return $secao->sigla_nome;
    }

    private function obtemTelefoneSecao(UnidadeOrganizacional $secao)
    {
        $telefone = $secao->contatos()->telefones()->first();

        if ($telefone) {
            return $telefone->valor;
        }

        return '';
    }

    private function obtemEnderecoSecao(UnidadeOrganizacional $secao)
    {
        $endereco = $secao->enderecos()->first();

        if ($endereco) {
            return $endereco->extenso();
        }
        
        return '';
    }

    private function obtemEmail(Servidor $servidor)
    {
        $email = $servidor->contatos()->emails()->first();
        if ($email) {
            $servidor->email = $email->valor;
        }

        return $servidor;
    }

    private function obtemEndereco(Servidor $servidor)
    {
        $endereco = $servidor->enderecos()->first();
        if ($endereco) {
            $servidor->endereco = $endereco->extenso();
        }

        return $servidor;
    }

    private function colunasSemRelacionamento() : Collection
    {
        return $this->colunas->filter(function ($coluna) {
            return $this->colunasRelacionamento()->search(strtolower($coluna->getName())) === false;
        })->map(function ($coluna) {
            return strtolower($coluna->getName());
        })->push('id');
    }

    private function nomesColunas() : Collection
    {
        return $this->colunas->map(function ($coluna) {
            return strtolower($coluna->getName());
        })->unique();
    }

    private function colunasRelacionamento() : Collection
    {
        return collect(['telefone', 'endereco', 'email'])->merge($this->colunasUOR);
    }
}
