<?php

namespace App\Services;

use App\Models\Disease;
use App\Http\Requests\CreateDiseaseRequest;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use App\Services\LogActionService;
use Illuminate\Support\Facades\Storage;

class DiseaseService
{
    private const DEFAULT_PER_PAGE = 10;
    private const MAX_PER_PAGE = 100;

    protected $fileStorage;
    
    public function __construct(
        FileStorageService $fileStorage)
    {
        $this->fileStorage = $fileStorage;
    }


    public function createDisease(array $data, $userId): array
    {
        try {
            DB::beginTransaction();
            
            $image_url = null;
            if (isset($data['cover_page']) && $data['cover_page']) {
                $disease = Disease::create([
                    'name' => $data['name'],
                    'deskripsi' => $data['deskripsi'],
                    'visibilitas' => $data['visibilitas'],
                    'schema' => $data['schema'],
                    'cover_page' => null,
                ]);

                $image_url = $this->fileStorage->storeCoverImage($data['cover_page'], $disease->id);
                
                $disease->update(['cover_page' => $image_url]);
            } else {
                $disease = Disease::create([
                    'name' => $data['name'],
                    'deskripsi' => $data['deskripsi'],
                    'visibilitas' => $data['visibilitas'],
                    'schema' => $data['schema'],
                    'cover_page' => null,
                ]);
            }

            DB::commit();

            LogActionService::logAction(
                $userId,
                'create',
                'Disease',
                $disease->id,
                $disease->toArray(),
                "Created disease: {$disease->name}",
                true
            );

            return [true, 'Disease created successfully.', $disease->toArray()];
        } catch (\Throwable $exception) {
            DB::rollBack();
            
            LogActionService::logAction(
                $userId,
                'create',
                'Disease',
                null,
                [
                    'name' => $data['name'] ?? null,
                    'error' => $exception->getMessage()
                ],
                "Failed to create disease: {$exception->getMessage()}",
                false
            );

            return [false, 'Disease creation failed: ' . $exception->getMessage(), []];
        }
    }

    public function editDisease($id, array $data, $userId): array
    {
        try {
            // var_dump($data);
            DB::beginTransaction();

            $disease = Disease::find($id);
            if (!$disease) {
                DB::rollBack();
                LogActionService::logAction(
                    $userId,
                    'edit',
                    'Disease',
                    $id,
                    ['error' => 'Disease not found'],
                    "Failed to edit disease: Disease not found",
                    false
                );
                return [false, 'Disease not found.', []];
            }

            $oldData = $disease->toArray();

            if (isset($data['cover_page']) && $data['cover_page']) {
                if ($disease->cover_page) {
                    $this->fileStorage->deleteFile($disease->cover_page, true);
                }

                $data['cover_page'] = $this->fileStorage->storeCoverImage($data['cover_page'], $disease->id);
            } else {
                unset($data['cover_page']);
            }

            $changedFields = array_keys(array_diff_assoc($data, $oldData));
            
            // Get only the values that are actually changing
            $relevantOldData = array_intersect_key($oldData, $data);
            $relevantNewData = array_intersect_key($data, $oldData);

            $disease->update($data);
            DB::commit();

            LogActionService::logAction(
                $userId,
                'edit',
                'Disease',
                $disease->id,
                [
                    'old' => $relevantOldData,
                    'new' => $relevantNewData,
                    'changed_fields' => $changedFields
                ],
                "Updated disease: {$disease->name}",
                true
            );

            return [true, 'Disease updated successfully.', $disease];
        } catch (\Throwable $exception) {
            DB::rollBack();

            LogActionService::logAction(
                $userId,
                'edit',
                'Disease',
                $id,
                [
                    'attempted_changes' => array_keys($data),
                    'error' => $exception->getMessage()
                ],
                "Failed to update disease: {$exception->getMessage()}",
                false
            );
            return [false, 'Disease update failed: ' . $exception->getMessage(), []];
        }
    }


    public function deleteDisease($id, $userId): array
    {
        try {
            DB::beginTransaction();
            $disease = Disease::find($id);
            if (!$disease) {
                DB::rollBack();
                LogActionService::logAction(
                    $userId,
                    'delete',
                    'Disease',
                    $id,
                    ['error' => 'Disease not found'],
                    "Failed to delete disease: Disease not found",
                    false
                );
                return [false, 'Disease not found.', []];
            }

            $diseaseInfo = [
                'name' => $disease->name,
                'had_cover_page' => !is_null($disease->cover_page),
                'schema_columns_count' => count($disease->schema['columns'] ?? [])
            ];

            if ($disease->cover_page) {
                $this->fileStorage->deleteFile($disease->cover_page, true);
            }

            $directoryPath = "diseases/records/$id";
            $this->fileStorage->deleteDirectory($directoryPath, true);

            $disease->delete();

            DB::commit();
            LogActionService::logAction(
                $userId,
                'delete',
                'Disease',
                $id,
                $diseaseInfo,
                "Deleted disease: {$disease->name}",
                true
            );
            return [true, 'Disease deleted successfully.', []];
        } catch (\Throwable $exception) {
            DB::rollBack();
            
            LogActionService::logAction(
                $userId,
                'delete',
                'Disease',
                $id,
                ['error' => $exception->getMessage()],
                "Failed to delete disease: {$exception->getMessage()}",
                false
            );
            return [false, 'Disease deletion failed: ' . $exception->getMessage(), []];
        }
    }
    
    public function getDisease(array $filters, $user): array
    {
        try {
            $query = Disease::query();
            $query->withCount('diseaseRecords');

            if ($user->role === 'operator') {
                $query->where('id', $user->disease_id);
            } elseif ($user->role === 'peneliti') {
                $query->where('visibilitas', 'publik');
            }

            $this->applyDiseaseFilters($query, $filters);
    
            $paginatedData = $this->paginateResults($query, $filters, 'diseases');
    
            return [true, 'Diseases retrieved successfully.', $paginatedData];
        } catch (\Throwable $exception) {
            return [false, 'Failed to retrieve diseases: ' . $exception->getMessage(), []];
        }
    }
    
    
    public function getDiseaseDetails($id): array
    {
        try {

            $user = auth()->user();

            $disease = Disease::withCount('diseaseRecords')->findOrFail($id);
    
            if (!$disease) {
                return [false, 'Disease not found.', []];
            }
    
            // Admins can access all diseases
            if ($user->role === 'admin') {
                return [true, 'Disease details retrieved successfully.', $disease];
            }
    
            // Operators can only access their assigned diseases
            if ($user->role === 'operator') {
                $managesDisease = $user->managed_diseases['disease_id'];
                if ($managesDisease != $id) {
                    return [false, 'Access denied. You are not assigned to this disease.', []];
                }
            }
    
            // Researchers can only access public diseases
            if ($user->role === 'peneliti') {
                if ($user->approval_status !== 'approved') {
                    return [false, 'Access denied. Your account is not approved.', []];
                }
                if ($disease->visibilitas !== 'publik') {
                    return [false, 'Access denied. This disease is private.', []];
                }
            }
    
            return [true, 'Disease details retrieved successfully.', $disease];
        } catch (\Throwable $exception) {
            return [false, 'Failed to retrieve disease details: ' . $exception->getMessage(), []];
        }
    }
    
    public function getStatistics(array $filters): array
    {
        try {
            $query = Disease::query();
            $query->withCount('diseaseRecords');

            $this->applyDiseaseFilters($query, $filters);

            $diseases = $query->get(['id', 'name', 'disease_records_count']);

            $totalDiseaseRecords = $diseases->sum('disease_records_count');

            $stats = [
                'total_disease_records' => $totalDiseaseRecords,
                'diseases' => $diseases->map(function ($disease) {
                    return [
                        'id' => $disease->id,
                        'name' => $disease->name,
                        'disease_records_count' => $disease->disease_records_count,
                    ];
                }),
            ];
    
            return [true, 'Disease statistics retrieved successfully.', $stats];
        } catch (\Throwable $exception) {
            return [false, 'Failed to retrieve statistics: ' . $exception->getMessage(), []];
        }
    }
    

    public function getSchemaField(int $diseaseId): array
    {
        try {
            [$success, $message, $disease] = $this->getDiseaseDetails($diseaseId);
            
            if (!$success) {
                return [[], $message];
            }
            
            $schema = $disease->schema['columns'] ?? null;
            
            if (!$schema) {
                return [[], $message];
            }
            
            $formattedSchema = [];
            foreach ($schema as $column) {
                $field = [
                    'name' => $column['name'] ?? null,
                    'type' => $column['type'] ?? null,
                ];
                
                if ($field['type'] === 'file') {
                    $field['format'] = $column['format'];

                    $field['multiple'] = isset($column['multiple']) ? (bool) $column['multiple'] : false; // Ensure multiple is set with default false
                }
                $field['is_visible'] = filter_var($column['is_visible'] ?? false, FILTER_VALIDATE_BOOLEAN);
                
                $formattedSchema[] = $field;
            }

            return [$formattedSchema, 'Schema Retrieved Successfully'];
        } catch (\Throwable $exception) {
            return []; 
        }
    }
    
    private function applyDiseaseFilters(Builder $query, array $filters): void
    {
        if (!empty($filters['visibilitas'])) {
            $query->where('visibilitas', $filters['visibilitas']);
        }

        if (!empty($filters['name'])) {
            $query->where('name', 'ILIKE', '%' . trim($filters['name']) . '%');
        }
        
        if (!empty($filters['column_type'])) {
            $query->whereJsonContains('schema->columns', ['type' => $filters['column_type']]);
        }
        
        if (!empty($filters['column_name'])) {
            $query->whereJsonContains('schema->columns', ['name' => $filters['column_name']]);
        }
        
        //Sorting
        $query->orderBy('updated_at', 'desc');
    }
    
    private function paginateResults(Builder $query, array $filters, string $itemsKey): array
    {
        $perPage = isset($filters['per_page']) && 
                is_numeric($filters['per_page']) && 
                  $filters['per_page'] > 0 && 
                  $filters['per_page'] <= self::MAX_PER_PAGE
            ? (int) $filters['per_page']
            : self::DEFAULT_PER_PAGE;

        $page = isset($filters['page']) && 
                is_numeric($filters['page']) && 
                $filters['page'] > 0
            ? (int) $filters['page']
            : 1;

        $results = $query->paginate($perPage, ['*'], 'page', $page);

        if ($results->isEmpty() && $results->currentPage() > 1) {
            $results = $query->paginate($perPage, ['*'], 'page', $results->lastPage());
        }

        return [
            $itemsKey => $results->items(),
            'pagination' => [
                'current_page' => $results->currentPage(),
                'last_page' => $results->lastPage(),
                'per_page' => (int) $results->perPage(),
                'total' => $results->total(),
                'from' => $results->firstItem(),
                'to' => $results->lastItem(),
                'has_more_pages' => $results->hasMorePages(),
            ]
        ];
    }
}
