<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Str;

/**
 * Model: RPartner (Partner Organization)
 * 
 * Represents an organizational partner with profile, contact info, and partnership details.
 * Supports public directory display and private management.
 * 
 * @property int $id
 * @property int|null $user_id
 * @property string $organization_name
 * @property string $slug
 * @property string|null $legal_name
 * @property string|null $description
 * @property string|null $mission_statement
 * @property string $email
 * @property string|null $phone
 * @property string|null $website
 * @property string $organization_type
 * @property string|null $category
 * @property string|null $logo
 * @property string|null $banner_image
 * @property array|null $social_media
 * @property \Carbon\Carbon|null $partnership_start_date
 * @property \Carbon\Carbon|null $partnership_end_date
 * @property string $partnership_status
 * @property string $partnership_level
 * @property array|null $services_offered
 * @property bool $is_verified
 * @property bool $show_in_directory
 * @property bool $is_featured
 */
class RPartner extends Model
{
    use HasFactory, SoftDeletes;

    protected $table = 'r_partners';

    protected $fillable = [
        'user_id',
        'organization_name',
        'slug',
        'legal_name',
        'description',
        'mission_statement',
        'email',
        'phone',
        'website',
        'address_line_1',
        'address_line_2',
        'city',
        'state_province',
        'postal_code',
        'country',
        'organization_type',
        'category',
        'logo',
        'banner_image',
        'social_media',
        'partnership_start_date',
        'partnership_end_date',
        'partnership_status',
        'partnership_level',
        'services_offered',
        'is_verified',
        'verified_at',
        'verified_by',
        'show_in_directory',
        'is_featured',
        'internal_notes',
    ];

    protected $casts = [
        'social_media' => 'array',
        'partnership_start_date' => 'date',
        'partnership_end_date' => 'date',
        'is_verified' => 'boolean',
        'verified_at' => 'datetime',
        'show_in_directory' => 'boolean',
        'is_featured' => 'boolean',
    ];

    /**
     * Organization type constants.
     */
    const TYPE_GOVERNMENT = 'government';
    const TYPE_NGO = 'ngo';
    const TYPE_PRIVATE_SECTOR = 'private_sector';
    const TYPE_ACADEMIC = 'academic';
    const TYPE_HEALTHCARE = 'healthcare';
    const TYPE_INTERNATIONAL = 'international';
    const TYPE_COMMUNITY_BASED = 'community_based';
    const TYPE_FAITH_BASED = 'faith_based';
    const TYPE_OTHER = 'other';

    /**
     * Partnership status constants.
     */
    const PARTNERSHIP_ACTIVE = 'active';
    const PARTNERSHIP_INACTIVE = 'inactive';
    const PARTNERSHIP_PENDING = 'pending';
    const PARTNERSHIP_SUSPENDED = 'suspended';

    /**
     * Partnership level constants.
     */
    const LEVEL_BRONZE = 'bronze';
    const LEVEL_SILVER = 'silver';
    const LEVEL_GOLD = 'gold';
    const LEVEL_PLATINUM = 'platinum';

    /**
     * Get the associated user account.
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id');
    }

    /**
     * Get all contacts for this partner.
     */
    public function contacts(): HasMany
    {
        return $this->hasMany(RPartnerContact::class, 'partner_id');
    }

    /**
     * Get primary contact.
     */
    public function primaryContact(): BelongsTo
    {
        return $this->contacts()->where('is_primary', true)->first();
    }

    /**
     * Get all partnerships (MOUs).
     */
    public function partnerships(): HasMany
    {
        return $this->hasMany(RPartnership::class, 'partner_id');
    }

    /**
     * Get active partnership.
     */
    public function activePartnership()
    {
        return $this->partnerships()->where('status', RPartnership::STATUS_ACTIVE)->latest()->first();
    }

    /**
     * Get forum threads by this partner.
     */
    public function forumThreads(): HasMany
    {
        return $this->hasMany(RForumThread::class, 'partner_id');
    }

    /**
     * Scope: Active partners.
     */
    public function scopeActive($query)
    {
        return $query->where('partnership_status', self::PARTNERSHIP_ACTIVE);
    }

    /**
     * Scope: Visible in directory.
     */
    public function scopeInDirectory($query)
    {
        return $query->where('show_in_directory', true)
                     ->where('partnership_status', self::PARTNERSHIP_ACTIVE);
    }

    /**
     * Scope: Featured partners.
     */
    public function scopeFeatured($query)
    {
        return $query->where('is_featured', true);
    }

    /**
     * Scope: Verified partners.
     */
    public function scopeVerified($query)
    {
        return $query->where('is_verified', true);
    }

    /**
     * Scope: By organization type.
     */
    public function scopeByType($query, string $type)
    {
        return $query->where('organization_type', $type);
    }

    /**
     * Check if partnership is active.
     */
    public function isPartnershipActive(): bool
    {
        if ($this->partnership_status !== self::PARTNERSHIP_ACTIVE) {
            return false;
        }

        if ($this->partnership_end_date && $this->partnership_end_date->isPast()) {
            return false;
        }

        return true;
    }

    /**
     * Check if partnership is expiring soon.
     */
    public function isPartnershipExpiringSoon(int $days = 30): bool
    {
        if (!$this->partnership_end_date) {
            return false;
        }

        return $this->partnership_end_date->isPast() === false &&
               $this->partnership_end_date->diffInDays(now()) <= $days;
    }

    /**
     * Get full address.
     */
    public function getFullAddressAttribute(): string
    {
        $parts = array_filter([
            $this->address_line_1,
            $this->address_line_2,
            $this->city,
            $this->state_province,
            $this->postal_code,
            $this->country,
        ]);

        return implode(', ', $parts);
    }

    /**
     * Get social media links.
     */
    public function getSocialLinksAttribute(): array
    {
        return $this->social_media ?? [];
    }

    /**
     * Get services offered as a guaranteed array.
     *
     * Handles all storage formats found in the DB:
     *  - Already an array (Eloquent hydrated it somehow)
     *  - Double-encoded JSON: "\"[\\\"Service A\\\",\\\"Service B\\\"]\"" → decode twice
     *  - Single-encoded JSON: "[\"Service A\",\"Service B\"]" → decode once
     *  - Comma-separated string: "Service A, Service B"
     */
    public function getServicesOfferedAttribute(mixed $value): array
    {
        if (is_array($value)) {
            return $value;
        }

        if (!is_string($value) || empty($value)) {
            return [];
        }

        // First decode attempt
        $decoded = json_decode($value, true);

        if (is_array($decoded)) {
            return $decoded;
        }

        // If first decode yielded a string, it was double-encoded — decode again
        if (is_string($decoded)) {
            $redecoded = json_decode($decoded, true);
            if (is_array($redecoded)) {
                return $redecoded;
            }
        }

        // Fall back: treat as comma-separated plain text
        return array_values(array_filter(array_map('trim', explode(',', $value))));
    }

    /**
     * Store services offered as a clean JSON array.
     */
    public function setServicesOfferedAttribute(mixed $value): void
    {
        if (is_array($value)) {
            $this->attributes['services_offered'] = json_encode(array_values($value));
        } elseif (is_string($value) && !empty($value)) {
            // Normalise via the getter logic then re-encode cleanly
            $this->attributes['services_offered'] = json_encode($this->getServicesOfferedAttribute($value));
        } else {
            $this->attributes['services_offered'] = null;
        }
    }

    /**
     * Get partnership level color.
     */
    public function getLevelColorAttribute(): string
    {
        return match($this->partnership_level) {
            self::LEVEL_BRONZE => 'orange',
            self::LEVEL_SILVER => 'gray',
            self::LEVEL_GOLD => 'yellow',
            self::LEVEL_PLATINUM => 'purple',
            default => 'gray',
        };
    }

    /**
     * Get organization type label.
     */
    public function getTypeLabelAttribute(): string
    {
        return match($this->organization_type) {
            self::TYPE_GOVERNMENT => 'Government Agency',
            self::TYPE_NGO => 'Non-Governmental Organization',
            self::TYPE_PRIVATE_SECTOR => 'Private Sector',
            self::TYPE_ACADEMIC => 'Academic Institution',
            self::TYPE_HEALTHCARE => 'Healthcare Organization',
            self::TYPE_INTERNATIONAL => 'International Organization',
            self::TYPE_COMMUNITY_BASED => 'Community-Based Organization',
            self::TYPE_FAITH_BASED => 'Faith-Based Organization',
            default => 'Other',
        };
    }

    /**
     * Generate slug from organization name.
     */
    public static function generateSlug(string $name): string
    {
        $slug = Str::slug($name);
        $count = static::where('slug', 'LIKE', "{$slug}%")->count();
        
        return $count > 0 ? "{$slug}-{$count}" : $slug;
    }
}
