<?php

namespace App\Services;

use App\Enums\FeatureEnum;
use App\Exceptions\ApiHttpException;
use App\Facades\OfficesService;
use App\Facades\WatchlistsService;
use App\Mailables\InvitationEmail;
use App\Models\Account;
use App\Models\Address;
use App\Models\AccountMember;
use App\Models\CompanyProfile;
use App\Models\Office;
use App\Models\Result;
use App\Models\Subscription;
use App\Models\SubscriptionPlan;
use App\Models\SubscriptionPlanOption;
use App\Models\User;
use Exception;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Intervention\Image\Drivers\Imagick\Driver;
use Intervention\Image\ImageManager;
use Throwable;

class AccountsService
{
    /**
     * Creates an Account for a given user
     *
     * @param User $user
     * @param array $accountData
     * @return Account
     */
    public function create(User $user, Array $accountData)
    {
        if ($user->ownAccount) {
            throw new ApiHttpException(400, trans('frontend.exception_owner_account_exists'));
        }

        DB::beginTransaction();
        try {
            $account = new Account();

            $account->type = Account::TYPE_REGULAR;
            $account->company = $accountData['companyName'];
            $account->tax_id = $accountData['taxId'];
            $account->owner()->associate($user);
            $account->save();

            $profile = new CompanyProfile();
            $profile->account()->associate($account);
            $profile->name = $account->company;
            $profile->employee_count = $accountData['numberOfEmployees'];
            $profile->phone = $accountData['phone'];
            $profile->phone_prefix = $accountData['phonePrefix'];
            $profile->email = $accountData['email'];
            $profile->is_active = false;
            $profile->save();

            if (!empty($accountData['specialisationIds'])) {
                $profile->companySpecialisations()->sync($accountData['specialisationIds']);
            }

            // If user is associated with a previous account, not owner, remove the association
            AccountMember::where('user_id', $user->id)->delete();

            $member = new AccountMember();
            $member->invitation_first_name = $user->first_name;
            $member->invitation_last_name = $user->last_name;
            $member->invitation_email = $user->email;
            $member->is_admin = true;
            $member->status = AccountMember::STATUS_ACCEPTED;
            $member->account()->associate($account);
            $member->user()->associate($user);
            $member->save();
            $accountAddress = $this->_createUpdateAddress(array_merge(['name' => $account->company], $accountData));
            $account->address()->associate($accountAddress);
            $account->save();

            // Create main office
            $office = new Office();
            $office->companyProfile()->associate($profile);
            $office->name = $account->company;
            $office->address = $accountData['address'];
            $office->floor = $accountData['floor'];
            $office->postal_code = $accountData['postalCode'];
            $office->location = $accountData['location'];
            $office->is_main_office = true;
            $office->email = $accountData['email'];
            $office->phone = $accountData['phonePrefix'] . ' ' . $accountData['phone'];
            $office->country_id = $accountData['countryId'];
            $office->save();

            $user->registration_step = User::REGISTRATION_STEP_PROFILE_SETUP;
            $user->save();

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

    /**
     * Updates the account registration details
     *
     * @param User $user
     * @param array $accountData
     * @return Account
     */

    public function updateAccountRegister(User $user, Array $accountData)
    {
        if (!$user->ownAccount) {
            throw new ApiHttpException(400, trans('frontend.exception_owner_account_not_exists'));
        }

        DB::beginTransaction();

        try {
            $account = $user->ownAccount;
            $account->company = $accountData['companyName'];
            $account->tax_id = $accountData['taxId'];
            $account->save();

            $profile = $account->profile;
            if (!$profile) {
                $profile = new CompanyProfile();
                $profile->account()->associate($account);
            }

            $profile->name = $account->company;
            $profile->employee_count = $accountData['numberOfEmployees'];
            $profile->phone = $accountData['phone'];
            $profile->phone_prefix = $accountData['phonePrefix'];
            $profile->email = $accountData['email'];
            $profile->save();

            if (!empty($accountData['specialisationIds'])) {
                $profile->companySpecialisations()->sync($accountData['specialisationIds']);
            }

            $accountAddress = $this->_createUpdateAddress(array_merge(['name' => $account->company], $accountData));
            $account->address()->associate($accountAddress);
            $account->save();

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

    /**
     * Invites a member to an account
     *
     * @param User $user
     * @param Array $memberData
     * @return AccountMember
     */
    public function inviteMember(User $user, Array $memberData): AccountMember
    {
        if (!$user->hasFeature(FeatureEnum::FULL_DASHBOARD, true)) {
            throw new ApiHttpException(403, trans('frontend.exception_no_permission'));
        }

        $account = $user->accountMember->account;

        // Check if owner user has active account
        if (!$account) {
            throw new ApiHttpException(403, trans('frontend.exception_user_doesnt_have_account'));
        }

        $subscription = $account->activeSubscriptions->first();

        // Checks for an active subscription
        if (!$subscription) {
            throw new ApiHttpException(403, trans('frontend.exception_no_active_subscription'));
        }

        // Checks if subscription supports user count
        if ($subscription->seats <= $account->members()->count()) {
            throw new ApiHttpException(403, trans('frontend.subscription_user_limit_reached'));
        }

        $member = AccountMember::where('invitation_email', $memberData['email'])
            ->where('account_id', $account->id)
            ->first();

        if ($member) {
            throw new ApiHttpException(403, trans('frontend.exception_member_already_invited'));
        }

        // Checks if user exists in the system
        $existingUser = User::where('email', $memberData['email'])->first();

        // User to add can't own an account
        if ($existingUser && $existingUser->ownAccount) {
            throw new ApiHttpException(403, trans('frontend.invited_user_has_own_account'));
        }

        $member = new AccountMember();
        $member->invitation_first_name = $memberData['firstName'];
        $member->invitation_last_name = $memberData['lastName'];
        $member->invitation_email = $memberData['email'];
        $member->is_admin = $memberData['isAdmin'];
        $member->status = AccountMember::STATUS_PENDING;
        $member->token = Str::random(60);
        $member->account()->associate($account);
        $member->save();

        // Send email to the invited user
        Mail::to($member->invitation_email)->send(new InvitationEmail($member));

        return $member;
    }

    /**
     * Returns all members of an account
     *
     * @param User $user
     * @return AccountMember
     */
    public function getMembers(User $user, Array $data)
    {
        if (!$user->hasFeature(FeatureEnum::FULL_DASHBOARD)) {
            throw new ApiHttpException(403, trans('frontend.exception_no_permission'));
        }

        $account = $user->accountMember->account;

        $query = $account->members()
            ->with('user');

        if (!empty($data['search'])) {
            $query->where(function ($query) use ($data) {
                $query->where('invitation_first_name', 'like', '%' . $data['search'] . '%')
                    ->orWhere('invitation_last_name', 'like', '%' . $data['search'] . '%')
                    ->orWhere('invitation_email', 'like', '%' . $data['search'] . '%')
                    ->orWhereHas('user', function ($query) use ($data) {
                        $query->where('first_name', 'like', '%' . $data['search'] . '%')
                            ->orWhere('last_name', 'like', '%' . $data['search'] . '%')
                            ->orWhere('email', 'like', '%' . $data['search'] . '%');
                    });
            });
        }

        $accountMembers = $query->paginate($data['itemsPerPage']);

        return $accountMembers;
    }

    public function adminGetMembers(Account $account, array $data)
    {
        $query = $account->members()
            ->with('user')
            ->orderBy('created_at', 'desc');

        if (!empty($data['search'])) {
            $query->where(function ($query) use ($data) {
                $query->where('invitation_first_name', 'like', '%' . $data['search'] . '%')
                    ->orWhere('invitation_last_name', 'like', '%' . $data['search'] . '%')
                    ->orWhere('invitation_email', 'like', '%' . $data['search'] . '%')
                    ->orWhereHas('user', function ($query) use ($data) {
                        $query->where('first_name', 'like', '%' . $data['search'] . '%')
                            ->orWhere('last_name', 'like', '%' . $data['search'] . '%')
                            ->orWhere('email', 'like', '%' . $data['search'] . '%');
                    });
            });
        }

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

    public function adminInviteMember(Account $account, array $memberData): AccountMember
    {
        $this->ensureMemberLimit($account);

        $member = AccountMember::where('invitation_email', $memberData['email'])
            ->where('account_id', $account->id)
            ->first();

        if ($member) {
            throw new ApiHttpException(403, trans('frontend.exception_member_already_invited'));
        }

        $existingUser = User::where('email', $memberData['email'])->first();

        if ($existingUser && $existingUser->ownAccount) {
            throw new ApiHttpException(403, trans('frontend.invited_user_has_own_account'));
        }

        $member = new AccountMember();
        $member->invitation_first_name = $memberData['firstName'];
        $member->invitation_last_name = $memberData['lastName'];
        $member->invitation_email = $memberData['email'];
        $member->is_admin = $memberData['isAdmin'];
        $member->status = AccountMember::STATUS_PENDING;
        $member->token = Str::random(60);
        $member->account()->associate($account);
        $member->save();

        Mail::to($member->invitation_email)->send(new InvitationEmail($member));

        return $member;
    }

    public function adminAttachMember(Account $account, array $memberData): AccountMember
    {
        $this->ensureMemberLimit($account);

        $user = User::find($memberData['userId']);

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

        if ($user->ownAccount) {
            throw new ApiHttpException(403, trans('frontend.invited_user_has_own_account'));
        }

        $existingByUser = AccountMember::where('user_id', $user->id)
            ->first();

        if ($existingByUser && $existingByUser->account_id != $account->id) {
            throw new ApiHttpException(403, trans('frontend.exception_user_already_has_account'));
        }

        $existingInAccount = $account->members()
            ->where(function ($query) use ($user) {
                $query->where('user_id', $user->id)
                    ->orWhere('invitation_email', $user->email);
            })
            ->first();

        if ($existingInAccount) {
            throw new ApiHttpException(403, trans('frontend.exception_member_already_invited'));
        }

        $member = new AccountMember();
        $member->invitation_first_name = $user->first_name;
        $member->invitation_last_name = $user->last_name;
        $member->invitation_email = $user->email;
        $member->is_admin = $memberData['isAdmin'];
        $member->status = AccountMember::STATUS_ACCEPTED;
        $member->accepted_at = now();
        $member->account()->associate($account);
        $member->user()->associate($user);
        $member->save();

        return $member;
    }

    public function adminUpdateMember(Account $account, int $memberId, array $memberData): AccountMember
    {
        $member = $account->members()->find($memberId);

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

        if ($this->isOwnerMember($account, $member)) {
            throw new ApiHttpException(403, 'Owner role cannot be changed. Transfer ownership at account level first.');
        }

        $member->is_admin = $memberData['isAdmin'];
        $member->save();

        return $member;
    }

    public function adminDeleteMember(Account $account, int $memberId): AccountMember
    {
        $member = $account->members()->find($memberId);

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

        if ($this->isOwnerMember($account, $member)) {
            throw new ApiHttpException(403, 'Owner cannot be removed from team. Change ownership first.');
        }

        $member->delete();

        return $member;
    }

    private function ensureMemberLimit(Account $account): void
    {
        $subscription = $account->activeSubscriptions->first();

        if (!$subscription) {
            throw new ApiHttpException(403, trans('frontend.exception_no_active_subscription'));
        }

        if ($subscription->seats <= $account->members()->count()) {
            throw new ApiHttpException(403, trans('frontend.subscription_user_limit_reached'));
        }
    }

    private function isOwnerMember(Account $account, AccountMember $member): bool
    {
        if (!empty($member->user_id) && (int) $member->user_id === (int) $account->owner_id) {
            return true;
        }

        $ownerEmail = $account->owner?->email;

        if (!$ownerEmail) {
            return false;
        }

        return $member->invitation_email === $ownerEmail;
    }

    /**
     * Accepts an invitation for a given user
     *
     * @param User $user
     * @param string $token
     * @return AccountMember
     */
    public function acceptInvitation(User $user, string $token): AccountMember
    {
        $member = AccountMember::where('token', $token)->first();

        if (!$member) {
            throw new ApiHttpException(403, trans('frontend.exception_invitation_not_found'));
        }

        if ($member->invitation_email != $user->email) {
            throw new ApiHttpException(403, trans('frontend.exception_invitation_email_mismatch'));
        }

        if ($user->accountMember) {
            throw new ApiHttpException(403, trans('frontend.exception_user_already_has_account'));
        }

        if ($member->status != AccountMember::STATUS_PENDING) {
            throw new ApiHttpException(403, trans('frontend.exception_invitation_already_accepted'));
        }

        $member->status = AccountMember::STATUS_ACCEPTED;
        $member->accepted_at = now();
        $member->user()->associate($user);
        $member->save();

        return $member;
    }

    /**
     * Removes a member from an account
     *
     * @param User $user - owner user
     * @param int $memberId - member Id to remove
     * @return AccountMember
     */
    public function deleteMember(User $user, int $memberId): AccountMember
    {
        if (!$user->hasFeature(FeatureEnum::FULL_DASHBOARD, true)) {
            throw new ApiHttpException(403, trans('frontend.exception_no_permission'));
        }

        $account = $user->accountMember->account;

        $member = $account->members()->find($memberId);

        if (!$member) {
            throw new ApiHttpException(403, trans('frontend.exception_member_not_found'));
        }

        if ($this->isOwnerMember($account, $member)) {
            throw new ApiHttpException(403, 'Owner cannot be removed from team. Change ownership first.');
        }

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

    /**
     * Updates a member from an account
     *
     * @param User $user - owner user
     * @param int $memberId - member Id to update
     * @return AccountMember
     */
    public function updateMember(User $user, array $memberData, int $memberId): AccountMember
    {
        if (!$user->hasFeature(FeatureEnum::FULL_DASHBOARD, true)) {
            throw new ApiHttpException(403, trans('frontend.exception_no_permission'));
        }

        $account = $user->accountMember->account;

        $member = $account->members()->find($memberId);

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

        if ($this->isOwnerMember($account, $member)) {
            throw new ApiHttpException(403, 'Owner role cannot be changed. Transfer ownership at account level first.');
        }

        $member->is_admin = $memberData['isAdmin'];
        $member->save();

        return $member;
    }

    /**
     * Updates the account address
     *
     * @param User $user
     * @param array $addressData
     * @return Account
     */
    public function createUpdateAccountAddress(User $user, Array $addressData): Account
    {
        $account = $user->ownAccount;

        if (!$account) {
            throw new ApiHttpException(403, trans('frontend.exception_user_doesnt_have_account'));
        }

        DB::beginTransaction();
        try {
            $address = $account->address;

            if (!$address) {
                $address = new Address();

                $user->registration_step = User::REGISTRATION_STEP_PROFILE_SETUP;
                $user->save();
            }

            $address->name = $addressData['name'];
            $address->address = $addressData['address'];
            $address->postal_code = $addressData['postalCode'];
            $address->location = $addressData['location'];
            $address->country_id = $addressData['countryId'];

            if (array_key_exists('floor', $addressData)) {
                $address->floor = $addressData['floor'];
            }
            if (array_key_exists('phonePrefix', $addressData)) {
                $address->phone_prefix = $addressData['phonePrefix'];
            }
            if (array_key_exists('phone', $addressData)) {
                $address->phone = $addressData['phone'];
            }
            $address->save();

            $account->address()->associate($address);

            if (array_key_exists('taxId', $addressData)) {
                $account->tax_id = $addressData['taxId'];
            }

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

    public function updateAccountBillingAddress(User $user, Array $data)
    {
        $account = $user->ownAccount;

        if (!$account) {
            throw new ApiHttpException(403, trans('frontend.exception_user_doesnt_have_account'));
        }

        $billingAddress = $account->billingAddress;

        if (!$billingAddress) {
            $billingAddress = new Address();
        }

        $billingAddress->name = $data['name'];
        $billingAddress->address = $data['address'];
        $billingAddress->floor = $data['floor'];
        $billingAddress->postal_code = $data['postalCode'];
        $billingAddress->location = $data['location'];
        $billingAddress->country_id = $data['countryId'];
        $billingAddress->phone_prefix = $data['phonePrefix'];
        $billingAddress->phone = $data['phone'];

        $billingAddress->save();

        return $billingAddress;
    }

    public function getAccountsForAdmin(Array $data)
    {
        $query = Account::query()
            ->select(['accounts.id', 'accounts.company', 'accounts.owner_id', 'accounts.created_at'])
            ->with(['owner', 'activeSubscriptions.subscriptionPlan']);

        if (!empty($data['search'])) {
            $query->where(function ($query) use ($data) {
                $query->where('company', 'like', '%' . $data['search'] . '%');

                $query->orWhereHas('owner', function ($query) use ($data) {
                    $query->where('first_name', 'like', '%' . $data['search'] . '%')
                        ->orWhere('last_name', 'like', '%' . $data['search'] . '%')
                        ->orWhere('email', 'like', '%' . $data['search'] . '%');
                });
            });
        }

        if (!empty($data['filter']['status'])) {
            $statuses = $data['filter']['status'];
            $query->whereHas('activeSubscriptions', function ($query) use ($statuses) {
                $query->where(function ($query) use ($statuses) {
                    foreach ($statuses as $status) {
                        switch ($status) {
                            case 'active_trial':
                                $query->orWhere(function ($query) use ($status) {
                                    $query->where('status', Subscription::STATUS_ACTIVE)
                                        ->where('trial_end_date', '>=', now())
                                        ->where('is_trial', true);
                                });
                                break;
                            case 'active_trial_expired':
                                $query->orWhere(function ($query) use ($status) {
                                    $query->where('status', Subscription::STATUS_ACTIVE)
                                        ->where('trial_end_date', '<', now())
                                        ->where('is_trial', true);
                                });
                                break;
                            case 'active_expired':
                                $query->orWhere(function ($query) use ($status) {
                                    $query->where('status', Subscription::STATUS_ACTIVE)
                                        ->where('end_date', '<', now())
                                        ->where('is_trial', false);
                                });
                                break;
                            default:
                                $query->orWhere('status', $status);
                                break;
                        }
                    }
                });
            });
        }

        if (!empty($data['filter']['plans'])) {
            $plans = $data['filter']['plans'];
            $query->whereHas('activeSubscriptions', function ($query) use ($plans) {
                $query->whereIn('subscription_plan_id', $plans);
            });
        }

        if (!empty($data['filter']['endDateFrom'])) {
            $query->whereHas('activeSubscriptions', function ($query) use ($data) {
                $query->where(function ($query) use ($data) {
                    $query->orWhere(function ($query) use ($data) {
                        $query->where('end_date', '>=', substr($data['filter']['endDateFrom'], 0, 10))
                            ->where('is_trial', false);
                    })->orWhere(function ($query) use ($data) {
                        $query->where('trial_end_date', '>=', substr($data['filter']['endDateFrom'], 0, 10))
                            ->where('is_trial', true);
                    });
                });
            });
        }

        if (!empty($data['filter']['endDateUntil'])) {
            $query->whereHas('activeSubscriptions', function ($query) use ($data) {
                $query->where(function ($query) use ($data) {
                    $query->orWhere(function ($query) use ($data) {
                        $query->where('end_date', '<=', substr($data['filter']['endDateUntil'], 0, 10))
                            ->where('is_trial', false);
                    })->orWhere(function ($query) use ($data) {
                        $query->where('trial_end_date', '<=', substr($data['filter']['endDateUntil'], 0, 10))
                            ->where('is_trial', true);
                    });
                });
            });
        }

        if (!empty($data['sort'])) {
            if ($data['sort']['key'] == 'endDate') {
                $query->leftJoin('subscriptions', function ($query) {
                    $query->select('end_date')
                        ->on('accounts.id', '=', 'subscriptions.account_id')
                        ->where('subscriptions.status', Subscription::STATUS_ACTIVE);
                })->orderBy('end_date', $data['sort']['order']);;
            } else {
                $query->orderBy($data['sort']['key'], $data['sort']['order']);
            }
        }

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

    /**
     * Creates an address to be used in accounts
     *
     * @param array $addressData
     * @return Address
     */
    protected function _createUpdateAddress(Array $addressData, Address $address = null): Address
    {
        $address = $address ?? new Address();
        $address->name = $addressData['name'];
        $address->address = $addressData['address'];
        $address->floor = $addressData['floor'];
        $address->postal_code = $addressData['postalCode'];
        $address->location = $addressData['location'];
        $address->country_id = $addressData['countryId'];
        $address->phone_prefix = $addressData['phonePrefix'];
        $address->phone = $addressData['phone'];
        $address->save();

        return $address;
    }

    /**
     * Updates an account addresses
     *
     * @param Account $account
     * @param array $data
     * @return Address
     */
    public function saveAddress(Account $account, Array $data)
    {
        $address = null;
        switch ($data['type']) {
            case 'company':
                $address = $account->address;
                break;
            case 'billing':

                $address = $account->billingAddress && $account->billingAddress->id != $account->address->id ? $account->billingAddress : null;
                break;
            case 'shipping':
                $address = $account->shippingAddress && $account->shippingAddress->id != $account->address->id ? $account->shippingAddress : null;
                break;
        }
        DB::beginTransaction();
        try {
            if ($data['sameAsCompany']) {
                if ($address && $address->id != $account->address_id) {
                    $address->delete();
                }
                $address = $account->address;
            } else {
                $address = $this->_createUpdateAddress($data, $address);
            }

            if ($data['type'] == 'company') {
                $account->address()->associate($address);
            } elseif ($data['type'] == 'billing') {
                $account->billingAddress()->associate($address);
            } elseif ($data['type'] == 'shipping') {
                $account->shippingAddress()->associate($address);
            }

            $account->save();

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

    public function adminUpdate(Account $account, Array $data)
    {
        $account->company = $data['company'];
        $account->type = $data['type'];
        $account->tax_id = $data['taxId'];
        $account->owner()->associate($data['ownerId']);
        $account->save();

        return $account;
    }

    public function getAccountPastSubscriptions(Account $account)
    {
        return $account->subscriptions()
            ->where('status', Subscription::STATUS_INACTIVE)
            ->with('subscriptionPlan')
            ->orderBy('end_date', 'desc')
            ->get();
    }

    public function cancelSubscription(Subscription $subscription, $data): Subscription
    {
        if (!$subscription->status == Subscription::STATUS_INACTIVE) {
            throw new ApiHttpException(400, 'Subscription is not active');
        }

        if ($data['cancelImmediately']) {
            $subscription->cancel_on = now();
            $subscription->status = Subscription::STATUS_INACTIVE;
        } else {
           $subscription->cancel_on = $data['cancellationDate'];
        }

        $subscription->save();

        return $subscription;
    }

    public function renewSubscription(Subscription $subscription): Subscription
    {
        if (!$subscription->status == Subscription::STATUS_INACTIVE) {
            throw new ApiHttpException(400, 'Subscription is not active, renew is not possible');
        }

        switch ($subscription->periodicity) {
            case Subscription::PERIODICITY_ANNUAL:
                if ($subscription->end_date > now()->addMonth(1)) {
                    throw new ApiHttpException(400, 'Subscription is too far in the future to renew');
                }
                $subscription->end_date = $subscription->end_date->addYear();
                break;
        }
        $subscription->renewed_date = now();
        $subscription->status = Subscription::STATUS_ACTIVE;
        $subscription->save();

        return $subscription;
    }

    public function activateSubscription(Subscription $subscription): Subscription
    {
        if ($subscription->status == Subscription::STATUS_ACTIVE && !$subscription->is_trial) {
            throw new ApiHttpException(400, 'Subscription is already active');
        }

        if ($subscription->status == Subscription::STATUS_INACTIVE && $subscription->account->activeSubscriptions->count() > 0) {
            throw new ApiHttpException(400, 'Account already has an active subscription');
        }

        $subscription->is_trial = false;
        $subscription->status = Subscription::STATUS_ACTIVE;
        $subscription->save();

        return $subscription;
    }

    public function saveSubscription(Subscription $subscription, Array $data)
    {
        if ($subscription->status != Subscription::STATUS_ACTIVE) {
            throw new ApiHttpException(400, 'Subscription is not active');
        }

        $subscription->subscriptionPlan()->associate($data['subscriptionPlanId']);
        $subscription->seats = $data['seats'];
        $subscription->price = $data['price'];
        $subscription->is_trial = $data['isTrial'];
        $subscription->save();

        return $subscription;
    }

    public function createSubscription(Account $account, Array $data)
    {
        if ($account->activeSubscriptions->first()) {
            throw new ApiHttpException(400, 'Account already has an active subscription');
        }

        $subscription = new Subscription();
        $subscription->subscriptionPlan()->associate($data['subscriptionPlanId']);
        $subscription->seats = $data['seats'];
        $subscription->price = $data['price'];
        $subscription->is_trial = $data['isTrial'];
        $subscription->periodicity = Subscription::PERIODICITY_ANNUAL;
        $subscription->start_date = now();
        if ($data['isTrial']) {
            $subscription->trial_end_date = now()->addDays(30);
            $subscription->end_date = now()->addYear()->addDays(30);
        } else {
            $subscription->end_date = now()->addYear();
        }
        $subscription->account()->associate($account);
        $subscription->status = Subscription::STATUS_ACTIVE;
        $subscription->save();

        return $subscription;
    }

    /**
     * Create account internally
     *
     * @param array $data
     * @return Account
     */
    public function createAccount(array $data): Account
    {
        DB::beginTransaction();
        try {
            if ($data['createUser']) {
                $user = new User();
                $user->first_name = $data['userFirstName'];
                $user->last_name = $data['userLastName'];
                $user->email = $data['userEmail'];

                $password = Str::password(10);
                $user->password = Hash::make($password );

                $user->status = User::STATUS_PENDING_EMAIL_CONFIRMATION;
                $user->save();

                WatchlistsService::createDefaultWatchlists($user);
            } else {
                $user = User::find($data['userId']);
            }

            $account = new Account();
            $account->company = $data['name'];
            $account->type = Account::TYPE_REGULAR;
            $account->owner()->associate($user);
            $account->save();

            $profile = new CompanyProfile();
            $profile->account()->associate($account);
            $profile->name = $data['name'];
            $profile->is_active = false;
            $profile->save();

            $member = new AccountMember();
            $member->invitation_first_name = $user->first_name;
            $member->invitation_last_name = $user->last_name;
            $member->invitation_email = $user->email;
            $member->is_admin = true;
            $member->status = AccountMember::STATUS_PENDING;
            $member->account()->associate($account);
            $member->user()->associate($user);
            $member->save();

            $plan = SubscriptionPlan::find($data['planId']);
            $planOption = !empty($data['optionId']) ? SubscriptionPlanOption::find($data['optionId']) : null;

            $subscription = new Subscription();
            $subscription->start_date = now();
            $subscription->is_trial = $data['hasTrial'];
            $subscription->subscription_reward_id = $data['subscriptionRewardId'] ?? null;

            if ($data['hasTrial']) {
                $subscription->trial_end_date = now()->addDays(30);
                $subscription->end_date = now()->addYear()->addDays(30);
            } else {
                $subscription->end_date = now()->addYear();
            }

            $subscription->status = Subscription::STATUS_ACTIVE;

            if ($planOption) {
                $subscription->price = $planOption->price;
                $subscription->periodicity = $planOption->periodicity;
                $subscription->seats = $planOption->seats;
            } else {
                $subscription->price = $data['price'];
                $subscription->periodicity = Subscription::PERIODICITY_ANNUAL;
                $subscription->seats = $data['seats'];
            }

            $subscription->subscriptionPlan()->associate($plan);
            $subscription->account()->associate($account);

            $subscription->save();
            DB::commit();
            return $account;
        } catch (Throwable $e) {
            DB::rollback();
            throw $e;
        }
    }

    public function getAccountResultWinners(User $user)
    {
        $account = $user->accountMember->account;

        $resultWinners = Result::whereHas('winners.entities.companyProfile.account', function ($query) use ($account) {
            $query->where('id', $account->id);
        })
        ->with([
            'winners' => function ($query) use ($account) {
                $query->whereHas('entities.companyProfile.account', function ($q) use ($account) {
                    $q->where('id', $account->id);
                });
            }
        ])->get();

        return $resultWinners;
    }

    public function getAccountResultEntities(User $user)
    {
        $account = $user->accountMember->account;

        $resultEntities = Result::whereHas('coordinationGroups.coordinationGroupMembers.companyProfile.account', function ($query) use ($account) {
            $query->where('id', $account->id);
        })
        ->with([
            'coordinationGroups.coordinationGroupMembers' => function ($query) use ($account) {
                $query->whereHas('companyProfile.account', function ($q) use ($account) {
                    $q->where('id', $account->id);
                });
            }
        ])
        ->get();

        return $resultEntities;
    }

    /**
     * Returns all invoices for the user's account
     *
     * @param User $user
     * @param array $data
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    public function getInvoices(User $user, array $data)
    {
        $account = $user->accountMember?->account;

        if (!$account) {
            throw new ApiHttpException(403, trans('frontend.no_account'));
        }

        $invoices = $account->invoices()
            ->orderBy('date', 'desc')
            ->paginate($data['itemsPerPage'], ['*'], 'page', $data['page']);

        return $invoices;
    }

    /**
     * Downloads an invoice PDF file with security validation
     *
     * @param User $user
     * @param int $invoiceId
     * @return \Symfony\Component\HttpFoundation\StreamedResponse
     */
    public function downloadInvoice(User $user, int $invoiceId)
    {
        $account = $user->accountMember?->account;

        if (!$account) {
            throw new ApiHttpException(403, trans('frontend.no_account'));
        }

        $invoice = $account->invoices()->where('id', $invoiceId)->first();

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

        if (!$invoice->media) {
            throw new ApiHttpException(404, trans('frontend.invoice_file_not_found'));
        }

        $media = $invoice->media;
        $filePath = storage_path('app/public/uploads/' . $media->base_folder . '/' . $media->path);

        if (!file_exists($filePath)) {
            throw new ApiHttpException(404, trans('frontend.invoice_file_not_found'));
        }

        $filename = $media->original_filename ?: ('invoice_' . $invoice->invoice_number . '.' . $media->extension);

        // Determine content type based on extension
        $contentTypes = [
            'pdf' => 'application/pdf',
            'doc' => 'application/msword',
            'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'xls' => 'application/vnd.ms-excel',
            'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'png' => 'image/png',
            'jpg' => 'image/jpeg',
            'jpeg' => 'image/jpeg',
            'gif' => 'image/gif',
            'zip' => 'application/zip',
        ];
        $contentType = $contentTypes[strtolower($media->extension)] ?? 'application/octet-stream';

        return response()->streamDownload(function () use ($filePath) {
            readfile($filePath);
        }, $filename, [
            'Content-Type' => $contentType,
            'Content-Disposition' => 'attachment; filename="' . $filename . '"',
        ]);
    }
}
