<?php

namespace App\Models;

use App\Enums\EstadoCivilEnum;
use App\Enums\FiltroBuscaServidorEnum;
use App\Enums\NivelEnsinoEnum;
use App\Enums\RegimeServidorEnum;
use App\Enums\SexoEnum;
use App\Enums\SituacaoServidorBuscaEnum;
use App\Traits\Atendivel;
use App\Traits\Contatavel;
use App\Traits\Conveniavel;
use App\Traits\TemProcessoJuridico;
use App\Traits\TemEndereco;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Notifications\Notifiable;
use OwenIt\Auditing\Auditable;
use OwenIt\Auditing\Contracts\Auditable as AuditableContract;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Facades\Image;

class Servidor extends Pessoa implements AuditableContract
{
    use Auditable;
    use Notifiable;
    use Contatavel;
    use Atendivel;
    use SoftDeletes;
    use Conveniavel;
    use TemProcessoJuridico;
    use TemEndereco;

    public const FIELD_MAP = [
        'nome' => 'Nome',
        'nome_social' => 'Nome social',
        'cpf' => 'CPF',
        'rg' => 'RG',
        'data_nascimento' => 'Data de nascimento',
        'nome_pai' => 'Nome do pai',
        'nome_mae' => 'Nome da mãe',
        'naturalidade' => 'Naturalidade',
        'nacionalidade' => 'Nacionalidade',
        'estado_civil' => 'Estado civil',
        'sexo' => 'Sexo',
        'nao_perturbe' => 'Não perturbe',
        'data_filiacao' => 'Data de filiação',
        'motivo_filiacao' => 'Motivo filiação',
        'motivo_desligamento' => 'Motivo deslig. sind.',
        'data_desligamento' => 'Data deslig. sind.',
        'tipo_desligamento' => 'Tipo deslig. sind.',
        'motivo_religamento_extraordinario' => 'Motivo relig. extraord.',
        'data_religamento_extraordinario' => 'Data relig. extraord.',
        'data_falecimento' => 'Data falecimento',
        'observacoes' => 'Observações',
        'imagem' => 'Imagem',
    ];

    protected $table = 'servidores';
    protected $guarded = ['id', 'created_at', 'updated_at'];
    protected $appends = ['nome_exibir'];
    protected $casts = ['nao_perturbe' => 'boolean'];
    protected $dates = ['data_nascimento'];

    public function tipo()
    {
        return 'associado';
    }

    public function eAssociado()
    {
        return $this->matriculas()->ativa()->first() != null;
    }

    public function eAposentado()
    {
        return $this->registros()->comRegime(RegimeServidorEnum::aposentado())->first() != null;
    }

    public function herdeiro()
    {
        return $this->hasOne(Herdeiro::class)->withDefault();
    }

    public function matriculas()
    {
        return $this->hasMany(Matricula::class);
    }

    public function registros()
    {
        return $this->hasMany(RegistroServidor::class);
    }

    public function registrosAtendimentos()
    {
        return $this->hasMany(RegistroAtendimento::class);
    }

    public function dependentes()
    {
        return $this->morphMany(Dependente::class, 'dependivel');
    }

    public function movimentacoes()
    {
        return $this->hasMany(MovimentacaoServidor::class);
    }

    public function documentos()
    {
        return $this->hasMany(DocumentoAssociado::class);
    }

    public function scopeComCargo($query, Cargo $cargo)
    {
        return $query->whereHas('registros', function ($query) use ($cargo) {
            $query->whereHas('cargo', function ($query) use ($cargo) {
                $query->where('id', $cargo->id);
            });
        });
    }

    public function scopeAlocadosNaSecretaria($query, UnidadeOrganizacional $secretaria)
    {
        $idsUnidades = $secretaria->hierarquiaInferior()->pluck('id')->merge($secretaria->id);

        return $query->whereHas('registros', function ($query) use ($idsUnidades) {
            $query->whereHas('unidadeOrganizacional', function ($query) use ($idsUnidades) {
                $query->whereIn('id', $idsUnidades);
            });
        });
    }

    public function matricula()
    {
        $matricula = $this->matriculas()->latest()->first();
        if ($matricula && !optional($matricula->tipo_desligamento)->isDefinitivo()) {
            return $matricula;
        }
    }

    public function matriculaDesligadaDefinitivamente()
    {
        $matricula = $this->matriculas()->latest()->first();
        if ($matricula && optional($matricula->tipo_desligamento)->isDefinitivo()) {
            return $matricula;
        }
    }

    public function saveImagem(string $imagemBase64)
    {
        $imagem = Image::make($imagemBase64);
        $filename = md5($imagem->__toString()) . '.jpg';
        Storage::put("servidores/{$filename}", $imagem->encode('jpg'));

        $miniatura = $imagem->resize(100, 75);
        Storage::put("servidores/miniaturas/{$filename}", $miniatura->encode('jpg'));
        $this->imagem = $filename;
    }

    public function removeImagem()
    {
        Storage::delete("servidores/$this->imagem");
        Storage::delete("servidores/miniaturas/$this->imagem");
        $this->imagem = '';
    }

    public function scopeAssociado($query)
    {
        $query->comSituacao(SituacaoServidorBuscaEnum::ativo());
    }

    public function desfiliadoTemporariamente()
    {
        $matricula = $this->matriculas()->latest()->first();
        return optional(optional($matricula)->tipo_desligamento)->isTemporario();
    }

    public function desfiliadoDefinitivamente()
    {
        $matricula = $this->matriculas()->latest()->first();
        return optional(optional($matricula)->tipo_desligamento)->isDefinitivo();
    }

    /**
     * Retorna se servidor tem alguma matricula, mesmo que desligada.
     */
    public function eAssociadoOuDesfiliado()
    {
        $matricula = $this->matriculas()->latest()->first();
        return $matricula != null;
    }

    public function situacao()
    {
        if ($this->eAssociado()) {
            return 'Associado';
        } elseif ($this->desfiliadoDefinitivamente()) {
            return 'Desfiliado definit.';
        } elseif ($this->desfiliadoTemporariamente()) {
            return 'Desfiliado temp.';
        }

        return 'Não associado';
    }

    public function scopeBuscaPorTermo($query, $termo, $excluidos = false)
    {
        $cpf = str_replace(['.', '-'], '', $termo);

        $query->where('nome', 'ilike', "%{$termo}%")
            ->orWhere('nome_social', 'ilike', "%{$termo}%")
            ->orWhere('cpf', 'ilike', $cpf)
            ->orWhereHas('registros', function ($query) use ($termo) {
                return $query->where('numero', $termo);
            });

        if ($excluidos) {
            $query->withTrashed();
        }

        return $query->orderBy('nome');
    }

    public function scopeBusca($query, FiltroBuscaServidorEnum $filtro, $busca, SituacaoServidorBuscaEnum $situacao = null, $excluidos = false)
    {
        if (!$situacao) {
            $situacao = SituacaoServidorBuscaEnum::ativo();
        }

        if ($filtro->isNome() && !empty($busca)) {
            $query->where(function ($query) use ($busca) {
                $query->where('nome', 'ilike', "%{$busca}%")
                    ->orWhere('nome_social', 'ilike', "%{$busca}%");
            });
        } elseif ($filtro->isRegistro() && !empty($busca)) {
            $query->where(function ($query) use ($busca) {
                $query->whereHas('registros', function ($query) use ($busca) {
                    $query->where('numero', $busca);
                });
            });
        } elseif ($filtro->isCpf() && !empty($busca)) {
            $cpf = str_replace(['.', '-'], '', $busca);
            $query->where('cpf', 'ilike', $cpf);
        } elseif ($filtro->isMatricula() &&  !empty($busca)) {
            $query->where(function ($query) use ($busca) {
                $query->whereHas('matriculas', function ($query) use ($busca) {
                    $query->where('id', $busca);
                });
            });
        }

        $query->comSituacao($situacao);

        if ($excluidos) {
            $query->withTrashed();
        }

        return $query->orderBy('nome');
    }

    public function scopeComSituacao($query, SituacaoServidorBuscaEnum $situacao)
    {
        if ($situacao->isAtivo()) {
            $query->where(function ($query) {
                $query->whereHas('matriculas', function ($query) {
                    $query->ultima()->ativa();
                });
            });
        }

        if ($situacao->isInativoTemporario()) {
            $query->where(function ($query) {
                $query->whereHas('matriculas', function ($query) {
                    $query->ultima()->desligadaTemporariamente();
                });
            });
        }

        if ($situacao->isInativoDefinitivo()) {
            $query->where(function ($query) {
                $query->whereHas('matriculas', function ($query) {
                    $query->ultima()->desligadaDefinitivamente();
                });
            });
        }

        if ($situacao->isNaoAssociado()) {
            $query->where(function ($query) {
                $query->whereDoesntHave('matriculas');
            });
        }

        return $query;
    }

    public function teveSeguroCanceladoAoDesfiliar()
    {
        $servidorTinhaSeguro = $this->convenios()->withTrashed()->seguro()->motivoExclusaoDesfiliacao()->count() > 0;
        $dependentesTinhamSeguro = $this->dependentes->filter(function ($dependente) {
            return $dependente->convenios()->withTrashed()->seguro()->motivoExclusaoDesfiliacao()->count() > 0;
        })->isNotEmpty();

        return $servidorTinhaSeguro || $dependentesTinhamSeguro;
    }

    public function teveOsanCanceladaAoDesfiliar()
    {
        $servidorTinhaOsan = $this->convenios()->withTrashed()->osan()->motivoExclusaoDesfiliacao()->count() > 0;
        $dependentesTinhamOsan = $this->dependentes->filter(function ($dependente) {
            return $dependente->convenios()->withTrashed()->osan()->motivoExclusaoDesfiliacao()->count() > 0;
        })->isNotEmpty();

        return $servidorTinhaOsan || $dependentesTinhamOsan;
    }

    public function scopeComRegistroAtivo($query)
    {
        return $query->whereHas('registros', function ($query) {
            return $query->ativos();
        });
    }

    public function scopeSemRegistroAtivo($query)
    {
        return $query->whereDoesntHave('registros', function ($query) {
            return $query->ativos();
        });
    }

    public function scopeNaoPerturbe($query, bool $naoPerturbe = true)
    {
        return $query->where('nao_perturbe', $naoPerturbe);
    }

    public function scopeComCelularMalaDireta($query)
    {
        $query->whereHas('contatos', function ($query) {
            return $query->celularMalaDireta();
        });

        return $query->with(['contatos' => function ($query) {
            return $query->celularMalaDireta();
        }]);
    }

    public function setCpfAttribute($cpf)
    {
        $this->attributes['cpf'] = str_replace(['.', '-'], '', $cpf);
    }

    public function getCpfAttribute($cpf)
    {
        return format_cpf($cpf);
    }

    public function setDataNascimentoAttribute($data)
    {
        if ($data) {
            $this->attributes['data_nascimento'] = Carbon::createFromFormat('d/m/Y', $data)->setTime(0, 0, 0, 0);
        }
    }

    public function getDataNascimentoAttribute()
    {
        if (!$this->attributes['data_nascimento']) {
            return null;
        }

        if ($this->attributes['data_nascimento'] instanceof Carbon) {
            return $this->attributes['data_nascimento'];
        }

        return Carbon::create($this->attributes['data_nascimento'])->setTime(0, 0, 0, 0);
    }

    public function setDataFalecimentoAttribute($data = null)
    {
        if ($data && $data instanceof \Datetime) {
            $this->attributes['data_falecimento'] = $data;
        } else if ($data) {
            $this->attributes['data_falecimento'] = Carbon::createFromFormat('d/m/Y', $data)->setTime(0, 0, 0, 0);
        } else {
            $this->attributes['data_falecimento'] = null;
        }
    }

    public function getDataFalecimentoAttribute()
    {
        if (!$this->attributes['data_falecimento']) {
            return null;
        }

        if ($this->attributes['data_falecimento'] instanceof Carbon) {
            return $this->attributes['data_falecimento'];
        }

        return Carbon::create($this->attributes['data_falecimento'])->setTime(0, 0, 0, 0);
    }

    public function setDataAdmissaoSindicatoAttribute($data)
    {
        $this->attributes['data_admissao_sindicato'] = $data ? Carbon::createFromFormat('d/m/Y', $data)->setTime(0, 0, 0, 0) : null;
    }

    public function getDataAdmissaoSindicatoAttribute()
    {
        if (isset($this->attributes['data_admissao_sindicato']) && $this->attributes['data_admissao_sindicato'] instanceof Carbon) {
            return $this->attributes['data_admissao_sindicato'];
        }

        return isset($this->attributes['data_admissao_sindicato']) ? Carbon::create($this->attributes['data_admissao_sindicato'])->setTime(0, 0, 0, 0) : null;
    }

    public function setDataDesligamentoSindicatoAttribute($data)
    {
        $this->attributes['data_desligamento_sindicato'] = $data ? Carbon::createFromFormat('d/m/Y', $data)->setTime(0, 0, 0, 0) : null;
    }

    public function getDataDesligamentoSindicatoAttribute()
    {
        if (isset($this->attributes['data_desligamento_sindicato']) && $this->attributes['data_desligamento_sindicato'] instanceof Carbon) {
            return $this->attributes['data_desligamento_sindicato'];
        }

        return isset($this->attributes['data_desligamento_sindicato']) ? Carbon::create($this->attributes['data_desligamento_sindicato'])->setTime(0, 0, 0, 0) : null;
    }

    public function getSexoAttribute()
    {
        if (!$this->attributes['sexo']) {
            return null;
        }

        return SexoEnum::make($this->attributes['sexo']);
    }

    public function setSexoAttribute(SexoEnum $sexo = null)
    {
        if ($sexo) {
            $this->attributes['sexo'] = $sexo->getIndex();
        }
    }

    public function getNivelEnsinoAttribute()
    {
        if (!$this->attributes['nivel_ensino']) {
            return null;
        }

        return NivelEnsinoEnum::make($this->attributes['nivel_ensino']);
    }

    public function setNivelEnsinoAttribute(NivelEnsinoEnum $nivel = null)
    {
        if ($nivel) {
            $this->attributes['nivel_ensino'] = $nivel->getIndex();
        }
    }

    public function getEstadoCivilAttribute()
    {
        if (!$this->attributes['estado_civil']) {
            return null;
        }

        return EstadoCivilEnum::make($this->attributes['estado_civil']);
    }

    public function setEstadoCivilAttribute(EstadoCivilEnum $estadoCivil = null)
    {
        if ($estadoCivil) {
            $this->attributes['estado_civil'] = $estadoCivil->getIndex();
        }
    }

    public function getEAssociadoAttribute()
    {
        return $this->eAssociado() ? 'SIM' : 'NÃO';
    }

    public function getNomeExibirAttribute()
    {
        if (isset($this->attributes['nome_social']) && $this->attributes['nome_social']) {
            return $this->attributes['nome_social'];
        }

        return $this->attributes['nome'];
    }

    public function routeNotificationForTwilio($notification)
    {
        if ($this->celularMalaDireta()) {
            return $this->celularMalaDireta()->getNumero();
        }

        return null;
    }
}
