<?php

namespace App\Services;

use App\Enums\FeatureEnum;
use App\Enums\JobOfferStatusEnum;
use App\Enums\WatchlistTypeEnum;
use App\Exceptions\ApiHttpException;
use App\Models\JobOffer;
use App\Models\User;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Throwable;

class JobOffersService
{
    /**
     * Creates a Job Offer
     *
     * @param User $user
     * @param array $jobOfferData
     * @return JobOffer
     */
    public function create(User $user, Array $jobOfferData)
    {
        if (!$user->hasFeature(FeatureEnum::JOBS, false, true)) {
            throw new ApiHttpException(403, trans('frontend.exception_no_permission'));
        }

        DB::beginTransaction();
        try {
            $profile = $user->accountMember?->account?->profile;
            if (!$profile) {
                throw new ApiHttpException(404, trans('frontend.exception_profile_not_found'));
            }

            $jobOffer = new JobOffer();
            $jobOffer->companyProfile()->associate($profile);
            $jobOffer->title = $jobOfferData['title'];
            $jobOffer->subtitle = $jobOfferData['subtitle'];
            $jobOffer->content = $jobOfferData['content'];
            $jobOffer->country_id = $jobOfferData['countryId'];
            $jobOffer->active_until = $jobOfferData['activeUntil'];
            $jobOffer->contact_person = $jobOfferData['contactPerson'];
            $jobOffer->contact_email = $jobOfferData['contactEmail'];
            $jobOffer->contact_by_email = $jobOfferData['contactByEmail'];
            $jobOffer->contact_by_letter = $jobOfferData['contactByLetter'];
            $jobOffer->contact_url = $jobOfferData['contactUrl'];
            $jobOffer->contact_name = $jobOfferData['contactName'];
            $jobOffer->contact_address = $jobOfferData['contactAddress'];
            $jobOffer->contact_postal_code = $jobOfferData['contactPostalCode'];
            $jobOffer->contact_city = $jobOfferData['contactCity'];
            $jobOffer->status = $jobOfferData['status'];
            $jobOffer->save();

            if (array_key_exists('specialisations', $jobOfferData)) {
                foreach ($jobOfferData['specialisations'] as $position => $specialisation) {
                    $jobOffer->specialisations()->attach($specialisation, ['position' => $position]);
                }
            }

            DB::commit();
            return $jobOffer;
        } catch (Throwable $e) {
            DB::rollback();
            throw $e;
        }
    }

    /**
     * Updates a Job Offer
     *
     * @param User $user
     * @param array $jobOfferData
     * @param int $jobOfferId
     * @return JobOffer
     */
    public function update(User $user, array $jobOfferData, int $jobOfferId)
    {
        if (!$user->hasFeature(FeatureEnum::JOBS, false, true)) {
            throw new ApiHttpException(403, trans('frontend.exception_no_permission'));
        }

        $jobOffer = JobOffer::with(['specialisations', 'country'])
            ->where('company_profile_id', $user->accountMember?->account?->profile?->id)
            ->where('id', $jobOfferId)->first();

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

        DB::beginTransaction();
        try {
            if (array_key_exists('title', $jobOfferData)) {
                $jobOffer->title = $jobOfferData['title'];
            }
            if (array_key_exists('subtitle', $jobOfferData)) {
                $jobOffer->subtitle = $jobOfferData['subtitle'];
            }
            if (array_key_exists('content', $jobOfferData)) {
                $jobOffer->content = $jobOfferData['content'];
            }
            if (array_key_exists('countryId', $jobOfferData)) {
                $jobOffer->country_id = $jobOfferData['countryId'];
            }
            if (array_key_exists('activeUntil', $jobOfferData)) {
                $jobOffer->active_until = $jobOfferData['activeUntil'];
            }
            if (array_key_exists('contactPerson', $jobOfferData)) {
                $jobOffer->contact_person = $jobOfferData['contactPerson'];
            }
            if (array_key_exists('contactEmail', $jobOfferData)) {
                $jobOffer->contact_email = $jobOfferData['contactEmail'];
            }
            if (array_key_exists('contactByEmail', $jobOfferData)) {
                $jobOffer->contact_by_email = $jobOfferData['contactByEmail'];
            }
            if (array_key_exists('contactByLetter', $jobOfferData)) {
                $jobOffer->contact_by_letter = $jobOfferData['contactByLetter'];
            }
            if (array_key_exists('contactUrl', $jobOfferData)) {
                $jobOffer->contact_url = $jobOfferData['contactUrl'];
            }
            if (array_key_exists('contactName', $jobOfferData)) {
                $jobOffer->contact_name = $jobOfferData['contactName'];
            }
            if (array_key_exists('contactAddress', $jobOfferData)) {
                $jobOffer->contact_address = $jobOfferData['contactAddress'];
            }
            if (array_key_exists('contactPostalCode', $jobOfferData)) {
                $jobOffer->contact_postal_code = $jobOfferData['contactPostalCode'];
            }
            if (array_key_exists('contactCity', $jobOfferData)) {
                $jobOffer->contact_city = $jobOfferData['contactCity'];
            }
            if (array_key_exists('status', $jobOfferData)) {
                $jobOffer->status = $jobOfferData['status'];
            }
            $jobOffer->save();

            if (array_key_exists('specialisations', $jobOfferData)) {
                $jobOffer->specialisations()->detach();
                foreach ($jobOfferData['specialisations'] as $position => $specialisation) {
                    $jobOffer->specialisations()->attach($specialisation, ['position' => $position]);
                }
            }

            DB::commit();
            $jobOffer->refresh();
            return $jobOffer;
        } catch (Throwable $e) {
            DB::rollback();
            throw $e;
        }
    }

    /**
     * Sets the status of a job offer
     *
     * @param User $user
     * @param int $jobOfferId
     * @param JobOfferStatusEnum $status
     * @return bool
     */
    public function setStatus(User $user, int $jobOfferId, JobOfferStatusEnum $status): bool
    {
        if (!$user->hasFeature(FeatureEnum::JOBS, false, true)) {
            throw new ApiHttpException(403, trans('frontend.exception_no_permission'));
        }

        $jobOffer = JobOffer::where('company_profile_id', $user->accountMember?->account?->profile?->id)->where('id', $jobOfferId)->first();

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

        $jobOffer->status = $status;
        $jobOffer->save();

        return true;
    }

    /**
     * Deletes a job offer
     *
     * @param User $user
     * @param int $jobOfferId
     * @return JobOffer
     */
    public function delete(User $user, int $jobOfferId): JobOffer
    {
        if (!$user->hasFeature(FeatureEnum::JOBS, false, true)) {
            throw new ApiHttpException(403, trans('frontend.exception_no_permission'));
        }

        $jobOffer = JobOffer::where('company_profile_id', $user->accountMember?->account?->profile?->id)->where('id', $jobOfferId)->first();

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

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

    /**
     * Gets a job offer
     *
     * @param int $jobOfferId
     * @return JobOffer
     */
    public function getJobOffer(int $jobOfferId): JobOffer
    {
        $jobOffer = JobOffer::with([
            'specialisations' => function ($query) {
                $query->full();
            },
            'country' => function ($query) {
                $query->full();
            }
        ])->where('status', JobOfferStatusEnum::ACTIVE)
            ->where('active_until', '>=', now())
            ->where('id', $jobOfferId)->first();

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

        return $jobOffer;
    }

    /**
     * Gets a job offer associated to the account
     *
     * @param User $user
     * @param int $jobOfferId
     * @return JobOffer
     */
    public function getAccountJobOffer(User $user, int $jobOfferId): JobOffer
    {
        if (!$user->hasFeature(FeatureEnum::JOBS, false, true)) {
            throw new ApiHttpException(403, trans('frontend.exception_no_permission'));
        }

        $jobOffer = JobOffer::with([
            'specialisations' => function ($query) {
                $query->full();
            },
            'country' => function ($query) {
                $query->full();
            }
        ])->where('company_profile_id', $user->accountMember?->account?->profile?->id)
            ->where('id', $jobOfferId)->first();

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

        return $jobOffer;
    }

    /**
     * Lists all job offers associated to the account
     *
     * @param User $user
     * @param array $params
     * @return LengthAwarePaginator
     */
    public function getAccountJobOffers(User $user, array $params)
    {
        if (!$user->hasFeature(FeatureEnum::JOBS, false, true)) {
            throw new ApiHttpException(403, trans('frontend.exception_no_permission'));
        }

        $page = $params['page'] ?? 1;
        $itemsPerPage = $params['itemsPerPage'] ?? 8;
        $orderDir = $params['orderDir'] ?? 'desc';

        $query = JobOffer::with([
            'specialisations' => function ($query) {
                $query->full();
            },
            'country' => function ($query) {
                $query->full();
            }
        ])->where('company_profile_id', $user->accountMember?->account?->profile?->id);

        if (array_key_exists('status', $params)) {
            $query->where('status', $params['status']);
        }

        $jobOffers = $query->orderBy('id', $orderDir)->paginate($itemsPerPage, ['*'], 'page', $page);
        return $jobOffers;
    }

    /**
     * Lists all job offers (Public - only active)
     *
     * @param User $user
     * @param array $params
     * @return LengthAwarePaginator
     */
    public function getJobOffers(array $params, $user = null)
    {
        $page = $params['page'] ?? 1;
        $itemsPerPage = $params['itemsPerPage'] ?? 8;
        $orderDir = $params['orderDir'] ?? 'desc';

        $query = JobOffer::with([
                'specialisations' => function ($query) {
                    $query->full();
                },
                'country' => function ($query) {
                    $query->full();
                }
            ])
            ->where('status', JobOfferStatusEnum::ACTIVE)
            ->where('active_until', '>=', now());

        $query->with(['watchlists' => function ($query) use ($user) {
            $query->where('user_id', $user ? $user->id : null)
                ->where('type', WatchlistTypeEnum::JOBS->value);
        }]);

        if (array_key_exists('status', $params)) {
            $query->where('status', $params['status']);
        }

        if (array_key_exists('search', $params)) {
            $query->where('title', 'like', '%' . $params['search'] . '%');
        }

        if (array_key_exists('specialisations', $params)) {
            $query->whereHas('specialisations', function ($query) use ($params) {
                $query->whereIn('id', $params['specialisations']);
            });
        }

        if (array_key_exists('countries', $params)) {
            $query->whereHas('country', function ($query) use ($params) {
                $query->whereIn('iso2', $params['countries']);
            });
        }

        $jobOffers = $query->orderBy('id', $orderDir)->paginate($itemsPerPage, ['*'], 'page', $page);
        return $jobOffers;
    }
}
