<?php

namespace App\Services;

use App\Enums\FeatureEnum;
use App\Exceptions\ApiHttpException;
use App\Models\CompanyProfile;
use App\Models\JobOffer;
use App\Models\Office;
use App\Models\Project;
use App\Models\ProjectImage;
use App\Models\User;
use Exception;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\Drivers\Imagick\Driver;
use Intervention\Image\ImageManager;

class CompanyProfilesService
{
    public function getBackofficeOffices(int $profileId)
    {
        $profile = CompanyProfile::find($profileId);

        if (!$profile) {
            throw new ApiHttpException(404, 'Company profile not found');
        }

        return Office::with('country')
            ->where('company_profile_id', $profileId)
            ->orderByDesc('is_main_office')
            ->orderBy('name')
            ->get();
    }

    public function createBackofficeOffice(int $profileId, array $data): Office
    {
        $profile = CompanyProfile::find($profileId);

        if (!$profile) {
            throw new ApiHttpException(404, 'Company profile not found');
        }

        DB::beginTransaction();
        try {
            $office = new Office();
            $office->companyProfile()->associate($profile);
            $this->fillOffice($office, $data);
            $office->save();

            DB::commit();
            return $office->fresh('country');
        } catch (Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public function updateBackofficeOffice(int $profileId, int $officeId, array $data): Office
    {
        $office = Office::where('company_profile_id', $profileId)->find($officeId);

        if (!$office) {
            throw new ApiHttpException(404, 'Office not found');
        }

        DB::beginTransaction();
        try {
            $this->fillOffice($office, $data);
            $office->save();

            DB::commit();
            return $office->fresh('country');
        } catch (Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public function deleteBackofficeOffice(int $profileId, int $officeId): bool
    {
        $office = Office::where('company_profile_id', $profileId)->find($officeId);

        if (!$office) {
            throw new ApiHttpException(404, 'Office not found');
        }

        $office->delete();

        return true;
    }

    public function getBackofficeProfiles(array $data)
    {
        $query = CompanyProfile::query()->with('account');

        if (!empty($data['search'])) {
            $search = $data['search'];
            $query->where(function ($q) use ($search) {
                $q->where('name', 'like', "%{$search}%")
                    ->orWhere('email', 'like', "%{$search}%")
                    ->orWhere('phone', 'like', "%{$search}%")
                    ->orWhereHas('account', function ($aq) use ($search) {
                        $aq->where('company', 'like', "%{$search}%");
                    });
            });
        }

        if (!empty($data['sort']['key'])) {
            $sortKey = $data['sort']['key'];
            $sortOrder = $data['sort']['order'] ?? 'asc';
            $sortable = ['id', 'name', 'email', 'is_active', 'updated_at'];
            if (in_array($sortKey, $sortable, true)) {
                $query->orderBy($sortKey, $sortOrder);
            }
        } else {
            $query->orderBy('id', 'desc');
        }

        return $query->paginate($data['itemsPerPage']);
    }

    public function createBackofficeProfile(array $data): CompanyProfile
    {
        $profile = new CompanyProfile();
        $this->fillBackofficeProfile($profile, $data);

        $isCoverImageUpdated = filter_var($data['isCoverImageUpdated'] ?? false, FILTER_VALIDATE_BOOLEAN);
        $isCompanyImageUpdated = filter_var($data['isCompanyImageUpdated'] ?? false, FILTER_VALIDATE_BOOLEAN);

        if (array_key_exists('coverImage', $data)) {
            $isCoverImageUpdated = true;
        }

        if (array_key_exists('companyImage', $data)) {
            $isCompanyImageUpdated = true;
        }

        $this->processCoverImage($profile, $data['coverImage'] ?? null, $isCoverImageUpdated);
        $this->processCompanyImage($profile, $data['companyImage'] ?? null, $isCompanyImageUpdated);

        $profile->save();
        $profile->companySpecialisations()->sync($data['specialisationIds'] ?? []);

        return $profile->fresh(['companySpecialisations']);
    }

    public function getBackofficeProfile(int $profileId): CompanyProfile
    {
        $profile = CompanyProfile::with('companySpecialisations')->find($profileId);

        if (!$profile) {
            throw new ApiHttpException(404, 'Company profile not found');
        }

        return $profile;
    }

    public function getBackofficeProfileByAccount(int $accountId): CompanyProfile
    {
        $profile = CompanyProfile::with('companySpecialisations')
            ->where('account_id', $accountId)
            ->first();

        if (!$profile) {
            $profile = new CompanyProfile();
            $profile->account_id = $accountId;
            $profile->is_active = false;
        }

        return $profile;
    }

    public function updateBackofficeProfile(int $profileId, array $data): CompanyProfile
    {
        $profile = CompanyProfile::find($profileId);

        if (!$profile) {
            throw new ApiHttpException(404, 'Company profile not found');
        }

        DB::beginTransaction();
        try {
            $this->fillBackofficeProfile($profile, $data);

            $isCoverImageUpdated = filter_var($data['isCoverImageUpdated'] ?? false, FILTER_VALIDATE_BOOLEAN);
            $isCompanyImageUpdated = filter_var($data['isCompanyImageUpdated'] ?? false, FILTER_VALIDATE_BOOLEAN);

            if (array_key_exists('coverImage', $data)) {
                $isCoverImageUpdated = true;
            }

            if (array_key_exists('companyImage', $data)) {
                $isCompanyImageUpdated = true;
            }

            $this->processCoverImage($profile, $data['coverImage'] ?? null, $isCoverImageUpdated);
            $this->processCompanyImage($profile, $data['companyImage'] ?? null, $isCompanyImageUpdated);

            $profile->save();
            $profile->companySpecialisations()->sync($data['specialisationIds'] ?? []);

            DB::commit();
            return $profile->fresh(['companySpecialisations']);
        } catch (Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    protected function fillBackofficeProfile(CompanyProfile $profile, array $data): void
    {
        $profile->account_id = $data['accountId'] ?? null;
        $profile->name = $data['name'];
        $profile->employee_count = $data['employeeCount'] ?? null;
        $profile->email = $data['email'] ?? null;
        $profile->phone_prefix = $data['phonePrefix'] ?? null;
        $profile->phone = $data['phone'] ?? null;
        $profile->website_url = $data['websiteUrl'] ?? null;
        $profile->subtitle = $data['subtitle'] ?? null;
        $profile->location = $data['location'] ?? null;
        $profile->description = $data['description'] ?? null;
        $profile->instagram_url = $data['instagramUrl'] ?? null;
        $profile->linkedin_url = $data['linkedinUrl'] ?? null;
        $profile->facebook_url = $data['facebookUrl'] ?? null;
        $profile->is_active = $data['isActive'];
    }

    protected function getProfileForAccount(User $user): CompanyProfile
    {
        $account = $user->accountMember?->account;
        $profile = $account?->profile;

        if (!$profile) {
            throw new ApiHttpException(404, trans('frontend.exception_profile_not_found'));
        }

        return $profile;
    }

    public function updateProfile(User $user, array $data): CompanyProfile
    {
        if (!$user->hasFeature(FeatureEnum::FULL_DASHBOARD, true)) {
            throw new ApiHttpException(403, trans('frontend.exception_no_permission'));
        }

        $profile = $this->getProfileForAccount($user);
        $image = $data['coverImage'] ?? null;
        $isImageUpdated = filter_var($data['isImageUpdated'], FILTER_VALIDATE_BOOLEAN);

        DB::beginTransaction();
        try {
            $this->processCoverImage($profile, $image, $isImageUpdated);

            $profile->companySpecialisations()->sync($data['specialisationIds'] ?? []);
            $profile->is_active = $data['isProfileActive'];
            $profile->description = $data['description'];
            $profile->name = $data['profileTitle'];
            $profile->employee_count = $data['numberOfEmployees'];
            $profile->save();

            DB::commit();
            return $profile->fresh(['companySpecialisations']);
        } catch (Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public function updateSocials(User $user, array $data): CompanyProfile
    {
        if (!$user->hasFeature(FeatureEnum::FULL_DASHBOARD, true)) {
            throw new ApiHttpException(403, trans('frontend.exception_no_permission'));
        }

        $profile = $this->getProfileForAccount($user);
        $profile->facebook_url = $data['facebookUrl'];
        $profile->linkedin_url = $data['linkedinUrl'];
        $profile->instagram_url = $data['instagramUrl'];
        $profile->save();

        return $profile->fresh(['companySpecialisations']);
    }

    public function uploadCompanyImage(User $user, ?UploadedFile $image): bool
    {
        if (!$user->hasFeature(FeatureEnum::FULL_DASHBOARD, true)) {
            throw new ApiHttpException(403, trans('frontend.exception_no_permission'));
        }

        $profile = $this->getProfileForAccount($user);

        DB::beginTransaction();
        try {
            $this->processCompanyImage($profile, $image, true);

            $profile->save();
            DB::commit();
            return true;
        } catch (Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    protected function processCoverImage(CompanyProfile $profile, ?UploadedFile $image, bool $isImageUpdated): void
    {
        if (!$isImageUpdated) {
            return;
        }

        $companyImagePath = config('path.internal.company_images');

        if (!Storage::exists($companyImagePath)) {
            Storage::makeDirectory($companyImagePath);
        }

        if ($profile->cover_image) {
            Storage::delete("{$companyImagePath}/{$profile->cover_image}");
            $profile->cover_image = null;
            $profile->cover_image_original = null;
            $profile->cover_image_size = null;
        }

        if ($image) {
            $imageName = Str::random() . '.' . $image->extension();
            $manager = new ImageManager(Driver::class);
            $img = $manager->read($image);
            $img->save(Storage::path("{$companyImagePath}/{$imageName}"), 90);
            $profile->cover_image = $imageName;
            $profile->cover_image_original = $image->getClientOriginalName();
            $profile->cover_image_size = $image->getSize();
        }
    }

    protected function processCompanyImage(CompanyProfile $profile, ?UploadedFile $image, bool $isImageUpdated): void
    {
        if (!$isImageUpdated) {
            return;
        }

        $companyImagePath = config('path.internal.company_images');

        if (!Storage::exists($companyImagePath)) {
            Storage::makeDirectory($companyImagePath);
        }

        if ($profile->company_image) {
            Storage::delete("{$companyImagePath}/{$profile->company_image}");
            $profile->company_image = null;
        }

        if ($image) {
            $imageName = Str::random() . '.' . $image->extension();
            $manager = new ImageManager(Driver::class);
            $img = $manager->read($image);
            $img->save(Storage::path("{$companyImagePath}/{$imageName}"), 90);
            $profile->company_image = $imageName;
        }
    }

    protected function fillOffice(Office $office, array $data): void
    {
        $office->name = $data['name'];
        $office->address = $data['address'];
        $office->postal_code = $data['postalCode'];
        $office->location = $data['location'];
        $office->floor = $data['floor'] ?? null;
        $office->link_google_maps = $data['linkGoogleMaps'] ?? null;
        $office->is_main_office = $data['isMainOffice'];
        $office->email = $data['email'] ?? null;
        $office->phone = $data['phone'];
        $office->fax = $data['fax'] ?? null;
        $office->website_url = $data['websiteUrl'] ?? null;
        $office->country_id = $data['countryId'];
    }

    // -------------------------------------------------------------------------
    // Backoffice Job Offers
    // -------------------------------------------------------------------------

    public function getBackofficeJobOffers(int $profileId)
    {
        $profile = CompanyProfile::find($profileId);

        if (!$profile) {
            throw new ApiHttpException(404, 'Company profile not found');
        }

        return JobOffer::with('specialisations')
            ->where('company_profile_id', $profileId)
            ->orderByDesc('created_at')
            ->get();
    }

    public function createBackofficeJobOffer(int $profileId, array $data): JobOffer
    {
        $profile = CompanyProfile::find($profileId);

        if (!$profile) {
            throw new ApiHttpException(404, 'Company profile not found');
        }

        DB::beginTransaction();
        try {
            $jobOffer = new JobOffer();
            $jobOffer->companyProfile()->associate($profile);
            $this->fillJobOffer($jobOffer, $data);
            $jobOffer->save();

            if (!empty($data['specialisationIds'])) {
                foreach ($data['specialisationIds'] as $position => $id) {
                    $jobOffer->specialisations()->attach($id, ['position' => $position]);
                }
            }

            DB::commit();
            return $jobOffer->fresh('specialisations');
        } catch (Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public function updateBackofficeJobOffer(int $profileId, int $jobOfferId, array $data): JobOffer
    {
        $jobOffer = JobOffer::with('specialisations')
            ->where('company_profile_id', $profileId)
            ->find($jobOfferId);

        if (!$jobOffer) {
            throw new ApiHttpException(404, 'Job offer not found');
        }

        DB::beginTransaction();
        try {
            $this->fillJobOffer($jobOffer, $data);
            $jobOffer->save();

            $jobOffer->specialisations()->detach();
            if (!empty($data['specialisationIds'])) {
                foreach ($data['specialisationIds'] as $position => $id) {
                    $jobOffer->specialisations()->attach($id, ['position' => $position]);
                }
            }

            DB::commit();
            $jobOffer->refresh();
            $jobOffer->load('specialisations');
            return $jobOffer;
        } catch (Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public function deleteBackofficeJobOffer(int $profileId, int $jobOfferId): bool
    {
        $jobOffer = JobOffer::where('company_profile_id', $profileId)->find($jobOfferId);

        if (!$jobOffer) {
            throw new ApiHttpException(404, 'Job offer not found');
        }

        $jobOffer->delete();
        return true;
    }

    protected function fillJobOffer(JobOffer $jobOffer, array $data): void
    {
        $jobOffer->title = $data['title'];
        $jobOffer->subtitle = $data['subtitle'];
        $jobOffer->content = $data['content'] ?? null;
        $jobOffer->country_id = $data['countryId'];
        $jobOffer->active_until = $data['activeUntil'];
        $jobOffer->contact_person = $data['contactPerson'];
        $jobOffer->contact_email = $data['contactEmail'];
        $jobOffer->contact_by_email = $data['contactByEmail'];
        $jobOffer->contact_by_letter = $data['contactByLetter'];
        $jobOffer->contact_url = $data['contactUrl'] ?? null;
        $jobOffer->contact_name = $data['contactName'];
        $jobOffer->contact_address = $data['contactAddress'];
        $jobOffer->contact_postal_code = $data['contactPostalCode'];
        $jobOffer->contact_city = $data['contactCity'];
        $jobOffer->status = $data['status'];
    }

    // -------------------------------------------------------------------------
    // Backoffice Projects
    // -------------------------------------------------------------------------

    public function getBackofficeProjects(int $profileId)
    {
        $profile = CompanyProfile::find($profileId);

        if (!$profile) {
            throw new ApiHttpException(404, 'Company profile not found');
        }

        return Project::with('images')
            ->where('company_profile_id', $profileId)
            ->orderByDesc('created_at')
            ->get();
    }

    public function createBackofficeProject(int $profileId, array $data): Project
    {
        $profile = CompanyProfile::with('members')->find($profileId);

        if (!$profile) {
            throw new ApiHttpException(404, 'Company profile not found');
        }

        DB::beginTransaction();
        try {
            $project = new Project();
            $project->companyProfile()->associate($profile);
            $this->fillProject($project, $profile, $data);
            $project->save();

            $this->processProjectImages($project, $data['images'] ?? []);

            DB::commit();
            return $project->fresh('images');
        } catch (Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public function updateBackofficeProject(int $profileId, int $projectId, array $data): Project
    {
        $project = Project::with('images')
            ->where('company_profile_id', $profileId)
            ->find($projectId);

        if (!$project) {
            throw new ApiHttpException(404, 'Project not found');
        }

        $profile = CompanyProfile::with('members')->find($profileId);
        if (!$profile) {
            throw new ApiHttpException(404, 'Company profile not found');
        }

        DB::beginTransaction();
        try {
            $this->fillProject($project, $profile, $data);
            $project->save();

            if (array_key_exists('images', $data)) {
                $this->processProjectImages($project, $data['images']);
            }

            DB::commit();
            return $project->fresh('images');
        } catch (Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public function deleteBackofficeProject(int $profileId, int $projectId): bool
    {
        $project = Project::with('images')
            ->where('company_profile_id', $profileId)
            ->find($projectId);

        if (!$project) {
            throw new ApiHttpException(404, 'Project not found');
        }

        $projectImagePath = config('path.internal.project_images');
        foreach ($project->images as $image) {
            $imagePath = "{$projectImagePath}/{$image->image}";
            if (Storage::exists($imagePath)) {
                Storage::delete($imagePath);
            }
        }

        $project->delete();
        return true;
    }

    protected function fillProject(Project $project, CompanyProfile $profile, array $data): void
    {
        $project->name = $data['name'];
        $project->content = $data['content'] ?? null;
        $project->project_date = $data['projectDate'] ?? null;

        if (!$project->user_id) {
            $resolvedUserId = $data['userId']
                ?? $profile->members
                    ->whereNotNull('user_id')
                    ->sortByDesc('is_admin')
                    ->first()?->user_id;

            if (!$resolvedUserId) {
                throw new ApiHttpException(422, 'No profile member available to associate as project owner.');
            }

            $project->user_id = $resolvedUserId;
        }
    }

    protected function processProjectImages(Project $project, array $images): void
    {
        $projectImagePath = config('path.internal.project_images');

        if (!Storage::exists($projectImagePath)) {
            Storage::makeDirectory($projectImagePath);
        }

        foreach ($images as $img) {
            if (isset($img['id']) && isset($img['deleted']) && $img['deleted']) {
                $existingImage = ProjectImage::where('project_id', $project->id)
                    ->where('id', $img['id'])
                    ->first();

                if ($existingImage) {
                    $imagePath = "{$projectImagePath}/{$existingImage->image}";
                    if (Storage::exists($imagePath)) {
                        Storage::delete($imagePath);
                    }

                    $existingImage->delete();
                }
            } elseif (isset($img['id']) && array_key_exists('position', $img)) {
                $existingImage = ProjectImage::where('project_id', $project->id)
                    ->where('id', $img['id'])
                    ->first();

                if ($existingImage) {
                    $existingImage->position = $img['position'];
                    $existingImage->save();
                }
            } elseif (isset($img['file']) && !isset($img['id'])) {
                $this->saveProjectImage($projectImagePath, $project, $img['file'], $img['position'] ?? null);
            }
        }
    }

    protected function saveProjectImage(string $projectImagePath, Project $project, UploadedFile $image, ?int $position = null): void
    {
        $imageName = Str::random() . '.' . $image->extension();
        $manager = new ImageManager(Driver::class);
        $img = $manager->read($image);

        $img->save(Storage::path("{$projectImagePath}/{$imageName}"), 90);

        $projectImage = new ProjectImage();
        $projectImage->project_id = $project->id;
        $projectImage->image = $imageName;
        $projectImage->image_original = $image->getClientOriginalName();
        $projectImage->image_size = $image->getSize();
        $projectImage->width = $img->width();
        $projectImage->height = $img->height();
        $projectImage->position = $position;
        $projectImage->save();
    }
}
