<?php

namespace Yuvayana\Acadlix\Common\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use Illuminate\Database\Capsule\Manager as DB;

defined('ABSPATH') || exit();

if (!class_exists('CourseEnrollment')) {
  class CourseEnrollment extends Model
  {

    const STATUS_ACTIVE = 'active';
    const STATUS_SUSPENDED = 'suspended';
    const STATUS_COMPLETED = 'completed';
    const STATUS_EXPIRED = 'expired';
    protected $table;

    protected $fillable = [
      'user_id',
      'course_id',
      'course_section_content_id',
      'progress_percent',
      'status', // active, suspended, completed, expired
      'started_at',
      'completed_at'
    ];

    /**
     * Cast attributes to native types or Carbon instances.
     */
    protected $casts = [
      'progress_percent' => 'integer',
      'started_at' => 'datetime',
      'completed_at' => 'datetime',
    ];

    public function __construct(array $attributes = [])
    {
      parent::__construct($attributes);

      $this->table = acadlix()->helper()->acadlix_table_prefix('course_enrollments');
    }

    public function scopeOfActive($query)
    {
      return $query->where('status', self::STATUS_ACTIVE);
    }

    public function scopeOfSuspended($query)
    {
      return $query->where('status', self::STATUS_SUSPENDED);
    }

    public function scopeOfCompleted($query)
    {
      return $query->where('status', self::STATUS_COMPLETED);
    }

    public function scopeOfExpired($query)
    {
      return $query->where('status', self::STATUS_EXPIRED);
    }

    public function scopeOfStatus($query, $status)
    {
      return $query->where('status', $status);
    }

    /* =========================================================================
     * BOOLEAN ACCESS HELPERS
     * ========================================================================= */

    public function isActive(): bool
    {
      return $this->status === self::STATUS_ACTIVE;
    }

    public function isCompleted(): bool
    {
      return $this->status === self::STATUS_COMPLETED;
    }

    public function isSuspended(): bool
    {
      return $this->status === self::STATUS_SUSPENDED;
    }

    public function isExpired(): bool
    {
      return $this->status === self::STATUS_EXPIRED;
    }

    public function user()
    {
      return $this->belongsTo(acadlix()->model()->wpUsers(), 'user_id', 'ID');
    }

    public function course()
    {
      return $this->belongsTo(acadlix()->model()->course(), 'course_id', 'ID');
    }

    public function enrollmentSources()
    {
      return $this->hasMany(acadlix()->model()->enrollmentSource(), 'enrollment_id', 'id');
    }

    public function hasAccess(int $userId, int $courseId): bool
    {
      $wpNow = Carbon::parse(current_time('mysql'));

      $enrollmentTable = $this->getTable();
      $enrollmentSourceTable = acadlix()->model()->enrollmentSource()->getTable();

      // Check if a valid, unexpired enrollment source join exists
      return DB::table($enrollmentTable . ' as e')
        ->join($enrollmentSourceTable . ' as es', 'es.enrollment_id', '=', 'e.id')
        ->where('e.user_id', $userId)
        ->where('e.course_id', $courseId)
        ->whereIn('e.status', [self::STATUS_ACTIVE, self::STATUS_COMPLETED])
        ->where(function ($query) use ($wpNow) {
          $query->whereNull('es.starts_at')
            ->orWhere('es.starts_at', '<=', $wpNow);
        })
        ->where(function ($query) use ($wpNow) {
          $query->whereNull('es.expires_at')
            ->orWhere('es.expires_at', '>=', $wpNow);
        })
        ->exists();
    }

    public function enroll(int $userId, int $courseId, array $args = []): self|null
    {
      // 1. Fetch current WP localized time for strict data integrity writes
      $wpNow = Carbon::parse(current_time('mysql'));

      $course = acadlix()->model()->course()->find($courseId);
      $user = acadlix()->model()->wpUsers()->find($userId);
      if(!$course || !$user){
        return null;
      }

      $convertToCarbon = function ($input) {
        if (empty($input))
          return null;

        // If the string contains only numbers and is exactly 10 digits, treat it as a Unix timestamp
        if (is_numeric($input) && strlen((string) $input) === 10) {
          return Carbon::createFromTimestamp((int) $input);
        }

        // Otherwise, parse it as a standard datetime string
        return Carbon::parse($input);
      };

      // 2. Extract operational arguments
      $sourceType = $args['source_type'] ?? 'order';
      $sourceId = $args['source_id'] ?? null;
      $type = $args['type'] ?? 'course';
      $itemId = $args['item_id'] ?? $courseId; // Default to the course itself if not specified
      $via = $args['via'] ?? "user";
      $viaId = $args['via_id'] ?? $userId; // Default to the user themselves if not specified
      $startsAt = isset($args['starts_at']) ? $convertToCarbon($args['starts_at']) : null;
      $expiresAt = isset($args['expires_at']) ? $convertToCarbon($args['expires_at']) : null;

      $courseSectionContentId = $args['course_section_content_id'] ?? null;
      $startedAt = isset($args['started_at']) ? $convertToCarbon($args['started_at']) : null;
      $completedAt = isset($args['completed_at']) ? $convertToCarbon($args['completed_at']) : null;


      // 3. Look for an existing lifecycle row to perform the Scenario A update
      $enrollment = self::where('user_id', $userId)
        ->where('course_id', $courseId)
        ->first();

      if ($enrollment) {
        $progress = isset($args['progress_percent']) ? (int) $args['progress_percent'] : $enrollment->progress_percent;
      } else {
        $progress = isset($args['progress_percent']) ? (int) $args['progress_percent'] : 0;
      }

      // 4. Determine Status based on Progress Percentage
      $calculatedStatus = ($progress >= 100) ? self::STATUS_COMPLETED : self::STATUS_ACTIVE;

      if ($enrollment) {
        $enrollment->update([
          'course_section_content_id' => $courseSectionContentId ?? $enrollment->course_section_content_id,
          'progress_percent' => $progress,
          'status' => $calculatedStatus,
          'updated_at' => $wpNow
        ]);

        $enrollmentSource = acadlix()->model()->enrollmentSource()
          ->where('enrollment_id', $enrollment->id)
          ->where('source_type', $sourceType)
          ->where('source_id', $sourceId)
          ->first();

        if ($enrollmentSource) {
          $enrollmentSource->update([
            'type' => $type,
            'item_id' => $itemId,
            'via' => $via,
            'via_id' => $viaId,
            'starts_at' => $startsAt,
            'expires_at' => $expiresAt
          ]);
        } else {
          acadlix()->model()->enrollmentSource()->create([
            'enrollment_id' => $enrollment->id,
            'source_type' => $sourceType,
            'source_id' => $sourceId,
            'type' => $type,
            'item_id' => $itemId,
            'via' => $via,
            'via_id' => $viaId,
            'starts_at' => $startsAt,
            'expires_at' => $expiresAt
          ]);
        }

        // Sync the current instance with the updated database data
        $this->fill($enrollment->toArray());
        $this->exists = true;

        return $this;
      }

      // 5. Fresh Enrollment Creation Flow
      $newEnrollment = self::create([
        'user_id' => $userId,
        'course_id' => $courseId,
        'course_section_content_id' => $courseSectionContentId,
        'progress_percent' => $progress,
        'status' => $calculatedStatus,
        'started_at' => $startedAt,
        'completed_at' => $completedAt,
      ]);

      // 6. Create the associated EnrollmentSource record
      acadlix()->model()->enrollmentSource()->create([
        'enrollment_id' => $newEnrollment->id,
        'source_type' => $sourceType,
        'source_id' => $sourceId,
        'type' => $type,
        'item_id' => $itemId,
        'via' => $via,
        'via_id' => $viaId,
        'starts_at' => $startsAt,
        'expires_at' => $expiresAt
      ]);

      // Sync instance state
      $this->fill($newEnrollment->toArray());
      $this->exists = true;

      return $this;
    }

    public function unenroll(int $userId, int $courseId, array $args = []): ?self
    {
      // Find the active lifecycle row
      $enrollment = self::where('user_id', $userId)
        ->where('course_id', $courseId)
        ->first();

      if (!$enrollment) {
        return null;
      }

      $enrollment->update([
        'status' => self::STATUS_SUSPENDED,
      ]);

      $this->fill($enrollment->toArray());
      return $this;
    }

    public function getEnrolledCourseQuery(int $userId)
    {
      $wpNow = Carbon::parse(current_time('mysql'));

      return $this->where('user_id', $userId)
        ->whereIn('status', [self::STATUS_ACTIVE, self::STATUS_COMPLETED])
        ->whereHas('enrollmentSources', function ($query) use ($wpNow) {
          $query->where(function ($query) use ($wpNow) {
            $query->whereNull('starts_at')
              ->orWhere('starts_at', '<=', $wpNow);
          })
            ->where(function ($query) use ($wpNow) {
              $query->whereNull('expires_at')
                ->orWhere('expires_at', '>=', $wpNow);
            });
        })
        ->orderByDesc('created_at')
        ->orderByDesc('id');
    }

    public function getEnrolledCourses(
      int $userId,
      $search = null,
      $skip = 0,
      $take = 10,
      $with = [],
      $categoryId = null
    ): array {

      if (empty($userId)) {
        return [
          'total' => 0,
          'courses' => [],
          'category_ids' => []
        ];
      }
      // 1. Initialize the pristine base query for this user
      $baseQuery = $this->getEnrolledCourseQuery($userId);

      // 2. Fetch ALL enrolled course IDs for this user to get their total category footprint
      // Cloning ensures we don't mutate the query instance we need for pagination later
      $allEnrolledCourseIds = (clone $baseQuery)->pluck('course_id')->unique()->toArray();

      // 3. Fetch ALL category IDs based on the complete list of enrolled courses
      $categoryIds = [];
      if (!empty($allEnrolledCourseIds)) {
        $termRelationshipsTable = acadlix()->model()->wpTermRelationship()->getTable();
        $categoryIds = DB::table($termRelationshipsTable)
          ->whereIn('object_id', $allEnrolledCourseIds)
          ->pluck('term_taxonomy_id')
          ->unique()
          ->values()
          ->toArray();
      }

      // 4. Now handle the main query filters (Search & Category)
      $coursesQuery = $baseQuery;

      if (!empty($search)) {
        $coursesQuery->whereHas('course', function ($q) use ($search) {
          $q->where('post_title', 'like', '%' . $search . '%');
        });
      }

      if (!empty($categoryId)) {
        $coursesQuery->whereHas('course', function ($q) use ($categoryId) {
          $q->whereHas('course_categories', function ($catQ) use ($categoryId) {
            $catQ->where('term_id', $categoryId);
          });
        });
      }

      // 5. Get the filtered total count for pagination
      $total = $coursesQuery->count();

      // 6. Fetch the paginated, filtered slice of courses
      $courses = $coursesQuery->with($with)->get();
      if($take > 0){
        $courses = $coursesQuery->with($with)->skip($skip)->take($take)->get();
      }
      return [
        'total' => $total,
        'courses' => $courses,
        'category_ids' => $categoryIds
      ];
    }

    public function getEnrolledUserQuery(int $courseId)
    {
      $wpNow = Carbon::parse(current_time('mysql'));

      return $this->where('course_id', $courseId)
        ->whereIn('status', [self::STATUS_ACTIVE, self::STATUS_COMPLETED])
        ->whereHas('enrollmentSources', function ($query) use ($wpNow) {
          $query->where(function ($query) use ($wpNow) {
            $query->whereNull('starts_at')
              ->orWhere('starts_at', '<=', $wpNow);
          })
            ->where(function ($query) use ($wpNow) {
              $query->whereNull('expires_at')
                ->orWhere('expires_at', '>=', $wpNow);
            });
        })
        ->orderByDesc('created_at')
        ->orderByDesc('id');
    }

    public function getEnrolledUsers(
      int $courseId,
      $search = null,
      $skip = 0,
      $take = 10,
      $with = [],
    ): array {

      if (empty($courseId)) {
        return [
          'total' => 0,
          'users' => [],
        ];
      }
      // 1. Initialize the pristine base query for this user
      $userQuery = $this->getEnrolledUserQuery($courseId);


      if (!empty($search)) {
        $userQuery->whereHas('user', function ($q) use ($search) {
          $q->where('display_name', 'like', '%' . $search . '%');
        });
      }

      $total = $userQuery->count();

      $users = $userQuery->with($with)->get();
      if($take > 0){
        $users = $userQuery->with($with)->skip($skip)->take($take)->get();
      }
      return [
        'total' => $total,
        'users' => $users,
      ];
    }

    public function delete()
    {
      // Delete associated enrollment sources first
      $this->enrollmentSources()->delete();

      // Then delete the enrollment itself
      return parent::delete();
    }

  }
}