<?php

namespace App\Models;

use Carbon\Carbon;
use App\Enums\Status;
use App\Notifications\VerifyEmail;
use Spatie\Permission\Models\Role;
use App\Notifications\ResetPassword;
use Illuminate\Support\Facades\Auth;
use Suiterus\Adg\Models\LongevityPay;
use Spatie\Permission\Traits\HasRoles;
use Tymon\JWTAuth\Contracts\JWTSubject;
use App\Models\Profile\ProfileBasicInfo;
use Illuminate\Notifications\Notifiable;
use Suiterus\Adg\Models\Reports\Reports;
use LaravelFeature\Featurable\Featurable;
use Suiterus\Adg\Models\HRExit\Clearance;
use Suiterus\Adg\Models\SM\ScheduleTitle;
use Suiterus\Adg\Models\Timekeeping\RFID;
use Suiterus\Dms\Models\Files\FileAccess;
use App\Models\Profile\UserProfilePicture;
use Suiterus\Adg\Models\Payroll\Allowance;
use Suiterus\Adg\Models\Salary\UserSalary;
use Suiterus\Adg\Models\SM\Branch_history;
use Suiterus\Adg\Models\Training\Training;
use Suiterus\Hrjp\Models\Position_history;
use Suiterus\Adg\Models\Payroll\SpecialPay;
use Suiterus\Adg\Models\SM\Division_history;
use Suiterus\Adg\Models\EMI\EmployeeMetaInfo;
use Suiterus\Adg\Models\SM\OfficePerEmployee;
use Suiterus\Hrjp\Models\Position_has_salary;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Suiterus\Adg\Models\SM\Department_history;
use Suiterus\Adg\Models\SM\DesignationHistory;
use Suiterus\Adg\Models\Training\UserTraining;

use Suiterus\Adg\Models\Approvals\UserOvertime;
use Suiterus\Adg\Models\EMI\EmployeeExtraField;

use Suiterus\Adg\Models\SM\Corporation_history;
use Suiterus\Adg\Models\Timekeeping\Attendance;

use Suiterus\Adg\Models\Timekeeping\Biometrics;
use LdapRecord\Laravel\Auth\LdapAuthenticatable;
use Suiterus\Adg\Models\Approvals\CTO\COCPoints;
use Suiterus\Adg\Models\Approvals\UserUndertime;
use Suiterus\Adg\Models\Payroll\PayrollEmployee;
use Suiterus\Adg\Models\SPMS\EmployeeEvaluation;
use Suiterus\Adg\Models\Salary\EmployeeAllowance;
use LaravelFeature\Featurable\FeaturableInterface;
use LdapRecord\Laravel\Auth\AuthenticatesWithLdap;
use Suiterus\Adg\Models\LearningDevelopment\Merit;
use Suiterus\Adg\Models\Salary\EmployeeSpecialPay;
use Suiterus\Adg\Models\Approvals\TrainingApproval;
use Suiterus\Adg\Models\EmployeeCases\EmployeeCase;
use Suiterus\Dms\Models\Repositories\SectionAccess;
use Suiterus\Adg\Models\EMI\EmployeeExtraFieldGroup;
use Suiterus\Adg\Models\LearningDevelopment\Demerit;
use Suiterus\Adg\Models\ServiceRecord\ServiceRecord;
use Suiterus\Adg\Models\Timekeeping\BiometricDevice;
use Suiterus\Adg\Models\UserSupervisors\Supervisors;
use Suiterus\Adg\Models\Approvals\CTO\CTOApplication;
use Suiterus\Adg\Models\Timekeeping\EmployeeSchedule;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Suiterus\Adg\Models\Timekeeping\BiometricRfidUser;
use Suiterus\Adg\Models\Timekeeping\TemporarySchedule;
use App\Models\AccessManagement\UserManagement\License;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Suiterus\Adg\Models\ActualDesignation;
use Suiterus\Adg\Models\LeaveManagement\Requests\Leave;

use Suiterus\Adg\Models\UserSupervisors\UserSupervisors;
use Suiterus\Adg\Models\LearningDevelopment\TrainingBond;
use Suiterus\Adg\Models\LeaveManagement\LeaveEligibleUser;
use Suiterus\Adg\Models\HRExit\ExitInterview\ExitInterview;
use Suiterus\Adg\Models\LeaveManagement\Crediting\LeaveBalance;
use Suiterus\Adg\Models\Timekeeping\Roster\RosterEmployeesPerGroup;
use Suiterus\Adg\Models\LeaveManagement\Crediting\LeaveCreditEarning;
use Suiterus\Adg\Models\LeaveManagement\Crediting\LeaveCreditDeduction;
use App\Models\AccessManagement\GroupManagement\Role as GroupManagementRole;
use Suiterus\Adg\Models\SPMS\UserMfo;
use Suiterus\Adg\Models\SPMS\UserPcrForm;

class User extends Authenticatable implements JWTSubject , MustVerifyEmail, FeaturableInterface, LdapAuthenticatable
{
    use Notifiable,
        HasFactory,
        HasRoles,
        Featurable,
        AuthenticatesWithLdap;

    protected $connection = 'mysql';

    protected $table = 'users';

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'current_role_id',
        'name',
        'email',
        'password',
        'isDark',
        'email_verified_at',
        'status',
        'guid'
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    /**
     * The accessors to append to the model's array form.
     *
     * @var array
     */
    protected $appends = [
        'photo_url',
        'is_supervisor'
    ];

    protected $with = [
        'currentRole',
        'roles',
        'permissions',
        'storage',
        'employeeMetaInfo',
        'supervisor',
        'user_supervisor',
        'exitInterview',
        'userProfilePicture',
        'profileBasicInfo'
    ];

    public function __construct(array $attributes = [])
    {
        $this->table = env('DB_DATABASE') . '.users';
        parent::__construct($attributes);
    }

    public function currentRole(){
        return $this->belongsTo(GroupManagementRole::class, 'current_role_id', 'id')->without(['member'])->with(['roleHasSystemModule.systemModule', 'roleHasPermission.permission']);
    }

    public function latest_evaluation() {
        return $this->hasOne(EmployeeEvaluation::class, 'employee_id', 'id')->without([
            'createdAudit',
        ])->latest();
    }

    public function evaluation() {
        return $this->hasMany(EmployeeEvaluation::class, 'employee_id', 'id')->latest();
    }

    // Allowances
    public function allowances()
    {
        return $this->belongsToMany(Allowance::class, 'employee_allowances', 'user_id', 'allowance_id')->withPivot('amount');
    }

    // Allowances
    public function special_pays()
    {
        return $this->belongsToMany(SpecialPay::class, 'employee_special_pays', 'user_id', 'special_pay_id')->withPivot('amount');
    }

    public function user_supervisor()
    {
        return $this->hasMany(UserSupervisors::class, 'user_id', 'id');
    }

    public function salary()
    {
        return $this->hasOne(UserSalary::class, 'user_id', 'id');
    }

    public function loan()
    {
        return $this->hasMany(UserLoan::class, 'user_id', 'id');
    }

    public function longevity_pay()
    {
        return $this->hasMany(LongevityPay::class, 'user_id', 'id');
    }

    public function employeeMetaInfo()
    {
        return $this->hasOne(EmployeeMetaInfo::class, 'user_id', 'id');
    }

    public function actualDesignation(){
        return $this->hasOne(ActualDesignation::class, 'user_id', 'id');
    }

    public function employeeExtraField()
    {
        return $this->hasMany(EmployeeExtraField::class, 'user_id', 'id');
    }


    public function exitInterview()
    {
        return $this->hasOne(ExitInterview::class, 'user_id', 'id');
    }

    public function clearance()
    {
        return $this->hasOne(Clearance::class, 'user_id', 'id');
    }

    public function positionHistory()
    {
        return $this->hasOne(Position_history::class, 'user_id', 'id');
    }

    public function corporationHistory()
    {
        return $this->hasOne(Corporation_history::class, 'user_id', 'id');
    }

    public function branchHistory()
    {
        return $this->hasOne(Branch_history::class, 'user_id', 'id');
    }

    public function divisionHistory()
    {
        return $this->hasOne(Division_history::class, 'user_id', 'id');
    }

    public function departmentHistory()
    {
        return $this->hasOne(Department_history::class, 'user_id', 'id');
    }

    public function biometric()
    {
        return $this->hasOne(Biometrics::class, 'user_id', 'id');
    }

    public function rfid()
    {
        return $this->hasOne(RFID::class, 'user_id', 'id');
    }

    public function designationHistory()
    {
        return $this->hasOne(DesignationHistory::class, 'user_id', 'id');
    }

    public function designationDate()
    {
        return $this->hasOne(DesignationHistory::class, 'user_id', 'id')->latest();
    }

    // The methods personalInformation, pdsChoices, and otherInformation refers to the personal information section in the PDS
    // This section is eloquent relationship for PDS
    public function personalInformation()
    {
        return $this->hasOne(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'personal_information');
    }

    public function pdsChoices()
    {
        return $this->hasOne(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'pds_choices');
    }

    public function otherInformation()
    {
        return $this->hasOne(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'other_information');
    }

    public function permanentAddress()
    {
        return $this->hasOne(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'permanent_address');
    }

    public function residentialAddress()
    {
        return $this->hasOne(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'residential_address');
    }

    public function spouse()
    {
        return $this->hasOne(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'spouse');
    }

    public function children()
    {
        return $this->hasMany(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'children');
    }

    public function father()
    {
        return $this->hasOne(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'father');
    }

    public function maidenName()
    {
        return $this->hasOne(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'maiden_name');
    }

    public function elementary()
    {
        return $this->hasMany(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'elementary');
    }

    public function secondary()
    {
        return $this->hasMany(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'secondary');
    }

    public function vocational()
    {
        return $this->hasMany(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'vocational');
    }

    public function college()
    {
        return $this->hasMany(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'college');
    }

    public function graduate()
    {
        return $this->hasMany(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'graduate');
    }

    public function civilService()
    {
        return $this->hasMany(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'civil_services');
    }

    public function workExperience()
    {
        return $this->hasMany(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'work_experience');
    }

    public function voluntaryWork()
    {
        return $this->hasMany(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'voluntary_work');
    }

    public function learningDevelopment()
    {
        return $this->hasMany(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'learning_development');
    }

    public function skillHobby()
    {
        return $this->hasMany(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'skills_hobbies');
    }

    public function choice()
    {
        return $this->hasOne(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'choices');
    }

    public function reference()
    {
        return $this->hasMany(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'reference');
    }

    public function governmentIssuedId()
    {
        return $this->hasMany(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'government_id');
    }

    public function fileAttachment()
    {
        return $this->hasOne(EmployeeExtraField::class, 'user_id', 'id')->where('table_name', 'file_attachments');
    }

    public function employeeSchedules()
    {
        return $this->hasMany(EmployeeSchedule::class, 'user_id', 'id');
    }

    public function employeeCases()
    {
        return $this->hasMany(EmployeeCase::class, 'user_id', 'id');
    }

    public function merits()
    {
        return $this->hasMany(Merit::class, 'user_id', 'id');
    }

    public function demerits()
    {
        return $this->hasMany(Demerit::class, 'user_id', 'id');
    }

    public function trainingBonds()
    {
        return $this->hasMany(TrainingBond::class, 'user_id', 'id');
    }

    public function attendance()
    {
        return $this->hasMany(Attendance::class, 'user_id', 'id');
    }

    public function biometric_rfid_record()
    {
        return $this->hasOne(BiometricRfidUser::class, 'user_id', 'id');
    }

    /**
     * Record of currently active and used temporary schedule
     */
    public function temporary_schedule()
    {
        $today = date('Y-m-d');
        return $this->hasOne(TemporarySchedule::class, 'user_id', 'id')
            ->where('status', Status::ACTIVE)
            ->where('end_date', '>=', $today)
            ->where('start_date', '<=', $today);
    }

    /**
     * Record of temporary schedule even when inactive -- for payroll
     */
    public function temporary_schedule_record()
    {
        return $this->hasOne(TemporarySchedule::class, 'user_id', 'id');
    }

    public function user_training()
    {
        return $this->hasMany(UserTraining::class, 'user_id', 'id');
    }
    public function overtime()
    {
        return $this->hasMany(UserOvertime::class, 'user_id', 'id');
    }
    public function undertime()
    {
        return $this->hasMany(UserUndertime::class, 'user_id', 'id');
    }
    public function user_training_latest()
    {
        return $this->hasOne(UserTraining::class, 'user_id', 'id')->latest();
    }
    public function training_approvals()
    {
        return $this->hasMany(TrainingApproval::class, 'employee_id', 'id');
    }
    public function supervisor()
    {
        return $this->hasOne(Supervisors::class, 'user_id', 'id');
    }
    public function leaves()
    {
        return $this->hasMany(Leave::class, 'user_id', 'id');
    }
    public function leave_balance()
    {
        return $this->hasMany(LeaveBalance::class, 'user_id', 'id');
    }
    public function leave_credit_deductions()
    {
        return $this->hasManyThrough(LeaveCreditDeduction::class, Leave::class, 'user_id', 'deductable_id', 'id', 'id');
    }
    public function leave_credit_earnings()
    {
        return $this->hasManyThrough(LeaveCreditEarning::class, LeaveBalance::class, 'user_id', 'leave_balance_id', 'id', 'id');
    }
    public function leaveEligibleUser()
    {
        return $this->hasMany(LeaveEligibleUser::class, 'user_id', 'id');
    }

    public function section_access()
    {
        return $this->hasMany(SectionAccess::class, 'user_id', 'id');
    }
    public function file_access()
    {
        return $this->hasMany(FileAccess::class, 'user_id', 'id');
    }

    public function license()
    {
        return $this->hasOne(License::class, 'user_id', 'id');
    }

    public function serviceRecord()
    {
        return $this->hasMany(ServiceRecord::class, 'user_id', 'id');
    }

    public function coc_points()
    {
        return $this->hasOne(COCPoints::class, 'user_id', 'id');
    }

    public function cto_applications()
    {
        return $this->hasMany(CTOApplication::class, 'user_id', 'id');
    }

    public function jobPost() {
        return $this->hasMany(Position_has_salary::class, 'hiring_manager', 'id');
    }

    public function employee_payroll_record() {
        return $this->hasMany(PayrollEmployee::class, 'user_id', 'id');
    }

    public function office() {
        return $this->hasOne(OfficePerEmployee::class, 'user_id', 'id');
    }

    /**
     * Get the profile photo URL attribute.
     *
     * @return string
     */
    public function getPhotoUrlAttribute()
    {
        return vsprintf('https://www.gravatar.com/avatar/%s.jpg?s=200&d=%s', [
            md5(strtolower($this->email)),
            $this->name ? urlencode("https://ui-avatars.com/api/$this->name") : 'mp',
        ]);
    }

    /**
     * Get the oauth providers.
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function oauthProviders()
    {
        return $this->hasMany(OAuthProvider::class);
    }

    /**
     * Send the password reset notification.
     *
     * @param  string  $token
     * @return void
     */
    public function sendPasswordResetNotification($token)
    {
        $this->notify(new ResetPassword($token));
    }

    /**
     * Send the email verification notification.
     *
     * @return void
     */
    public function sendEmailVerificationNotification()
    {
        $this->notify(new VerifyEmail);
    }

    /**
     * @return int
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }

    //notifications
    public function notifications()
    {
        return $this->hasMany('App\Models\Systems\NotificationTo');
    }

    //storage
    public function storage()
    {
        return $this->hasOne('Suiterus\Dms\Models\Users\Storage');
    }


    // Methods
    public function getLeavePointsEarnedAttribute()
    {
        $balances = $this->leave_balance()->with('credit_earnings')->get();
        $points = 0;
        foreach ($balances as $balance) {
            foreach ($balance->credit_earnings as $earning) {
                $points += $earning->credits;
            }
        }
        return $points;
    }

    public function getLeavePointsDeductedAttribute()
    {
        $deductions = $this->leave_credit_deductions()->sum('credits');
        return number_format($deductions, 2);
    }

    public function userProfilePicture()
    {
        return $this->hasOne(UserProfilePicture::class, 'user_id', 'id');
    }

    public function profileBasicInfo()
    {
        return $this->hasOne(ProfileBasicInfo::class, 'user_id', 'id');
    }

    public function getIsSupervisorAttribute(){
        return Supervisors::where('user_id', $this->id)->first() ? true : false;
    }

    public function reportable()
    {
        return $this->morphOne(Reports::class, 'entity');
    }

    public function rosterEmployeesPerGroup(){
        return $this->hasMany(RosterEmployeesPerGroup::class, 'user_id', 'id');
    }

    public function getFirstNameAttribute(){
        return $this->personalInformation()->first()->employeeExtraFieldColumn()->where('field_name', 'first_name')->first()->field_value ?? 'N/A';
    }

    public function getMiddleNameAttribute(){
        return $this->personalInformation()->first()->employeeExtraFieldColumn()->where('field_name', 'middle_name')->first()->field_value ?? 'N/A';
    }

    public function getLastNameAttribute(){
        return $this->personalInformation()->first()->employeeExtraFieldColumn()->where('field_name', 'last_name')->first()->field_value ?? 'N/A';
    }

    public function getGenderAttribute(){
        return $this->pdsChoices()->first() ? $this->pdsChoices()->first()->employeeExtraFieldColumn()->where('field_name', 'sex')->first()->field_value : 'N/A';
    }

    public function employeeExtraFieldGroup(){
        return $this->hasMany(EmployeeExtraFieldGroup::class, 'user_id', 'id');
    }

    public function pcrForms(){
        return $this->hasMany(UserPcrForm::class, 'user_id', 'id');
    }

    public function mfos(){
        return $this->hasMany(UserMfo::class, 'user_id', 'id');
    }

    public function hasModules($module){
        $systemModules = $this->currentRole->roleHasSystemModule()->with(['systemModule'])->get();

        $moduleGuards = $systemModules->map(function ($systemModule) {
            return $systemModule->systemModule->name;
        })->toArray();

        $modules = is_array($module)
            ? $module
            : explode('|', $module);

        foreach ($modules as $module) {
            if (in_array($module, $moduleGuards)) {
                return true;
            }
        }

        return false;
    }
}
