<?php

namespace App\Modules\Clients;

use App\Models\ValidationModel;
use App\Models\Follow;
use App\Models\Keychain;
use App\Models\Sla;
use App\Models\Contract;
use App\Models\User;
use App\Modules\Clients\Site;
use App\Modules\Clients\SiteUser;
use App\Modules\Bookings\Task;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Casts\Attribute;
use App\Modules\Clients\ContractTask;
use App\Modules\Invoices\Sale;
use App\ContractsTask;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;


class Client extends \Eloquent
{
    use SoftDeletes;
    use \Venturecraft\Revisionable\RevisionableTrait;
    use HasFactory;

    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'clients';

    /**
     * The attributes that should be mutated to dates.
     *
     * @var array
     */
    protected $dates = ['deleted_at'];

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

    /**
     * The name used in polimorphisms.
     *
     * @var string
     */
    protected $morphClass = 'client';

    /**
     * The relations to eager load on every query.
     *
     * @var array
     */
    protected $with = [];

    /**
     *	The attributes that won't be assigned during
     *	a mass assignment.
     */
    protected $fillable = [
        'name', 'primary_site', 'primary_contact', 'contract_id', 'contract_price', 'contract_start_date', 'contract_end_date', 'contract_monthly_allowance',
        'sla_id', 'website', 'followup_exempt', 'blind_notes', 'flagged', 'dynamics_id','onSite','emergency','recurring','contractStart','isRecurring'
    ];

    /**
     *	Format the contract start date according to mysql standards.
     */
    public function setContractStartDateAttribute($value): void
    {
        $this->attributes['contract_start_date'] = Carbon::parse($value)->toDateTimeString();
    }

    /**
     *	Format the contract start date when retrieved from the database.
     */
    public function getContractStartDateAttribute($value): string
    {
        $date = Carbon::parse($value)->format('d-m-Y');

        if (! validateDate($date, 'd-m-Y')) {
            $date = Carbon::now()->format('d-m-Y');
        }

        return $date;
    }

    /**
     *	Format the contract end date according to mysql standards.
     */
    public function setContractEndDateAttribute($value): void
    {
        $this->attributes['contract_end_date'] = Carbon::parse($value)->toDateTimeString();
    }

    /**
     *	Format the contract end date when retrieved from the database.
     */
    public function getContractEndDateAttribute($value): string
    {
        $date = Carbon::parse($value)->format('d-m-Y');

        if (! validateDate($date, 'd-m-Y')) {
            $date = Carbon::now()->format('d-m-Y');
        }

        return $date;
    }

    /**
     * Retrieves the main email address from the main user within the company.
     */
    public function getEmailAttribute(): string
    {
        $id = $this->id;

        // We need to determine the email address
        // and to do so we will first get a list of sites that have users
        $site_user = SiteUser::whereNotNull('email')->whereHas('site', function ($q) use ($id) {
            $q->where('sites.client_id', $id);
        })->first();

        return $site_user ? $site_user->email : '';
    }

    public function getTypeAttribute(): string
    {
        return 'client';
    }

    public function follow(int $user_id): void
    {
        $follow = new Follow();
        $follow->user_id = $user_id;
        $follow->followable_id = $this->id;
        $follow->followable_type = 'App\Modules\Clients\Client';
        $follow->save();
    }

    // ==================================================================
    //
    // 		primary_contact -> SET & GET
    //
    // ------------------------------------------------------------------

        /**
         *	Returns an empty string if the value is null.
         */
        public function getPrimaryContactAttribute($value): ?int
        {
            return $value ? (int) $value : null;
        }

        /**
         *	If the value is empty then set it as a NULL value.
         */
        public function setPrimaryContactAttribute($value): void
        {
            $this->attributes['primary_contact'] = $value ? (int) $value : null;
        }

    // ==================================================================
    //
    // 		sla_id -> SET & GET
    //
    // ------------------------------------------------------------------

        /**
         *	Returns an empty string if the value is null.
         */
        public function getSlaIdAttribute($value): ?int
        {
            return $value ? (int) $value : null;
        }

        /**
         *	If the value is empty then set it as a NULL value.
         */
        public function setSlaIdAttribute($value): void
        {
            $this->attributes['sla_id'] = $value ? (int) $value : null;
        }

    // ==================================================================
    //
    // 		contract_id -> SET & GET
    //
    // ------------------------------------------------------------------

        /**
         *	Returns an empty string if the value is null.
         */
        public function getContractIdAttribute($value): ?int
        {
            return $value ? (int) $value : null;
        }

        /**
         *	If the value is empty then set it as a NULL value.
         */
        public function setContractIdAttribute($value): void
        {
            $this->attributes['contract_id'] = $value ? (int) $value : null;
        }

    // ==================================================================
    //
    // 		primary_site -> SET & GET
    //
    // ------------------------------------------------------------------

        /**
         *	Returns an empty string if the value is null.
         */
        public function getPrimarySiteAttribute($value): ?int
        {
            return $value ? (int) $value : null;
        }

        /**
         *	If the value is empty then set it as a NULL value.
         */
        public function setPrimarySiteAttribute($value): void
        {
            $this->attributes['primary_site'] = $value ? (int) $value : null;
        }

    public function sites(): HasMany
    {
        return $this->hasMany(Site::class);
    }

    /**
     * Get all tasks for this client (polymorphic relationship).
     *
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
     */
    public function tasks(): MorphMany
    {
        return $this->morphMany(Task::class, 'client', 'client_type', 'client_id');
    }

    public function primary_site(): BelongsTo
    {
        return $this->belongsTo(Site::class, 'primary_site');
    }

    public function primary_contact(): BelongsTo
    {
        return $this->belongsTo(SiteUser::class, 'primary_contact');
    }

    public function sla(): BelongsTo
    {
        return $this->belongsTo(Sla::class, 'sla_id');
    }

    public function contract(): BelongsTo
    {
        return $this->belongsTo(\App\Modules\Admin\Contract::class, 'contract_id');
    }

    /**
     * Returns the ContractsTask that belongs to the client (legacy relationship for compatibility).
     *
     * @return BelongsTo
     */
    public function ContractsTask(): BelongsTo
    {
        return $this->belongsTo(ContractsTask::class, 'contract_id');
    }

    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'client_user_subscriptions')->withTimestamps();
    }

    public function keychains(): MorphMany
    {
        return $this->morphMany(Keychain::class, 'keychainable');
    }

    /**
     *	Retrieves the SLA assigned to this client, if any.
     *
     *	@return (Eloquent Model)
     */
    public function contract_task(): HasMany
    {
        return $this->hasMany(ContractTask::class, 'client_id');
    }
    /**
     * Get all sales for this client (polymorphic relationship).
     *
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
     */
    public function sales(): MorphMany
    {
        return $this->morphMany(Sale::class, 'client', 'client_type', 'client_id');
    }

    /**
     * Returns the list of the users currently subscribed to the client
     *
     * @return \User
     */
    public function subscribers(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'client_user_subscriptions', 'client_id', 'user_id');
    }

    /**
     * Get all devices for this client (polymorphic relationship).
     *
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
     */
    public function devices(): MorphMany
    {
        return $this->morphMany(\App\Modules\Bookings\Device::class, 'client', 'client_type', 'client_id');
    }

    /**
     * Get all projects for this client (polymorphic relationship).
     *
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
     */
    public function projects(): MorphMany
    {
        return $this->morphMany(\App\Modules\Bookings\Project::class, 'client', 'client_type', 'client_id');
    }

    /**
     * Get all messages for this client (polymorphic relationship).
     *
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
     */
    public function messages(): MorphMany
    {
        return $this->morphMany(\App\Modules\Bookings\Message::class, 'client', 'client_type', 'client_id');
    }

    /**
     * Search clients by a given term.
     *
     * @param  $query
     * @param  $term
     *
     * @return Illuminate\Database\Query\Builder
     */
    public function scopeSearch($query, string $term)
    {
        return $query->select('clients.id', 'clients.name', 'clients.flagged')
                ->leftJoin('sites', 'sites.client_id', '=', 'clients.id')
                ->leftJoin('site_users', 'site_users.site_id', '=', 'sites.id')
                ->groupBy('clients.id')
                ->whereNull('deleted_at')
                ->where(function ($query) use ($term) {
                    return $query->where('clients.name', 'like', '%'.$term.'%')
                        ->orWhere('sites.primary_phone', 'like', $term.'%')
                        ->orWhere('site_users.phone', 'like', $term.'%')
                        ->orWhere('site_users.mobile', 'like', $term.'%');
                });
    }

    /**
     * Returns the monthly allowance left for this client in minutes
     * for the current month (restored original business logic).
     *
     * @return float|null
     */
    public function monthlyAllowanceLeft(): ?float
    {
        if (!$this->contract_monthly_allowance) {
            return null;
        }

        // Original logic: sum time_spent for tasks created this month
        $time_spent = $this->tasks()
            ->where('created_at', '>=', date('Y-m-1 00:00:00'))
            ->sum('time_spent');

        return floatval($this->contract_monthly_allowance - $time_spent);
    }

    /**
     * Calculate the emergency allowance used for the client in hours
     * (exact copy of original Laravel5 logic).
     *
     * @return float
     */
    public function EmergencyUsed(): float
    {
        // Return 0 if contractStart is not set
        if (!$this->contractStart) {
            return 0.0;
        }

        // Original Laravel5 logic: get tasks within contract period
        $time_spent = $this->tasks()
            ->whereDate('onsite_start', '<', Carbon::today())
            ->whereDate('onsite_start', '>', Carbon::today()->startOfMonth()->subMonth()->day((int)$this->contractStart))    
            ->get();
        
        $output = 0;
        foreach ($time_spent->toArray() as $task) { 
            if (isset($task['recurring']) && $task['recurring'] == 1) {
                continue; // skip if recurring is true
            }
            if (!isset($task['onsite_start']) || !isset($task['onsite_end'])) {
                continue; // skip if dates are missing
            }
            $start = Carbon::parse($task['onsite_start']);
            $end = Carbon::parse($task['onsite_end']);
            $diff = $start->diff($end);
            $hours = $diff->h;
            $hours = $hours + ($diff->days * 24);
            $output += $hours;
        }
        return $output;
    }

    /**
     * Calculate the recurring allowance used for the client in hours
     * (exact copy of original Laravel5 logic).
     *
     * @return float
     */
    public function RecurringUsed(): float
    {
        // Return 0 if contractStart is not set
        if (!$this->contractStart) {
            return 0.0;
        }

        // Original Laravel5 logic: get tasks within contract period
        $time_spent = $this->tasks()
            ->whereDate('onsite_start', '<', Carbon::today())
            ->whereDate('onsite_start', '>', Carbon::today()->startOfMonth()->subMonth()->day((int)$this->contractStart))    
            ->get();
        
        $output = 0; 
        foreach ($time_spent->toArray() as $task) { 
            if (isset($task['recurring']) && $task['recurring'] == 0) {
                continue; // skip if recurring is not true
            }
            if (!isset($task['onsite_start']) || !isset($task['onsite_end'])) {
                continue; // skip if dates are missing
            }
            $start = Carbon::parse($task['onsite_start']);
            $end = Carbon::parse($task['onsite_end']);
            $diff = $start->diff($end);
            $hours = $diff->h;
            $hours = $hours + ($diff->days * 24);
            $output += $hours;
        }
        return $output;
    }

}
