<?php

namespace App\Repositories;

use Carbon\Carbon;

class CommonFilterRepository
{
    /**
     * Filter with direct fields on the query
     *
     * @param object $query
     * @param string $field
     * @param object $value
     *
     * @return object
     */
    public function filterWithDirectFields($query, $field, $value)
    {
        if ($value == '' || $value == []) {
            return $query;
        }

        if (is_array($value)) {
            return $query->whereIn($field, $value);
        }

        return $query->where($field, $value);
    }

    /**
     * Filter the query based on a relation's fields using the whereHas method.
     *
     * @param object $query The query object to filter.
     * @param string $whereHasRelation The name of the relation to check for existence.
     * @param string $field The field on the relation to compare.
     * @param mixed $userStatus The value to compare the field against.
     * @return object The filtered query object.
     */
    public function filterWithWhereHasRelationFields($query, $whereHasRelation, $field, $value)
    {
        if ($value == '' || $value == []) {
            return $query;
        }

        return $query->whereHas($whereHasRelation, function ($query) use ($field, $value) {

            if (is_array($value)) {
                $query->whereIn($field, $value);
            } else {
                $query->where($field, $value);
            }
        });
    }

    /**
     * Filter the query based on nested relation's fields using the whereHas method.
     *
     * @param object $query The query object to filter.
     * @param string $whereHasRelation1 The first relation to check for existence.
     * @param string $whereHasRelation2 The second relation to check for existence.
     * @param string $field The field on the relation to compare.
     * @param mixed $userStatus The value to compare the field against.
     *
     * @return object The filtered query object.
     */
    public function filterWithNestedWhereHasRelationFields($query, $whereHasRelation1, $whereHasRelation2, $field, $value)
    {
        if ($value == '' || $value == []) {
            return $query;
        }

        return $query->whereHas($whereHasRelation1, function ($query) use ($whereHasRelation2, $field, $value) {
            $query->whereHas($whereHasRelation2, function ($query) use ($field, $value) {

                if (is_array($value)) {
                    $query->whereIn($field, $value);
                } else {
                    $query->where($field, $value);
                }
            });
        });
    }

    /**
     * Filter the query based on direct date range fields
     *
     * @param object $query The query object to filter
     * @param string $field The field on the model to compare
     * @param string $from The start date of the date range
     * @param string $to The end date of the date range
     *
     * @return object The filtered query object
     */
    public function filterWithDirectDateRange($query, $field, $from = '', $to = '')
    {
        if ($from) {
            $query = $query->whereDate($field, '>=', convert_to_db_date($from));
        }

        if ($to) {
            $query = $query->whereDate($field, '<=', convert_to_db_date($to));
        }

        return $query;
    }

    /**
     * Filter the query based on relational date range fields
     *
     * @param object $query The query object to filter
     * @param string $relation The name of the relation to compare
     * @param string $field The field on the related model to compare
     * @param string $from The start date of the date range
     * @param string $to The end date of the date range
     *
     * @return object The filtered query object
     */
    public function filterWithRelationalDateRange($query, $relation, $field, $from = '', $to = '')
    {
        $query = $query->whereHas($relation, function ($query) use ($field, $from, $to) {
            if ($from) {
                $query->whereDate($field, '>=', convert_to_db_date($from));
            }

            if ($to) {
                $query->whereDate($field, '<=', convert_to_db_date($to));
            }
        });

        return $query;
    }

    /**
     * Searches for a given search text in multiple fields of the query.
     *
     * @param object $query The query object to search in.
     * @param array $fields An array of fields to search in.
     * @param string $searchText The text to search for.
     * @return object The modified query object.
     */
    public function searchToMultipleFields($query, array $fields, string $searchText)
    {
        if ($searchText) {
            $query->where(function ($query) use ($searchText, $fields) {
                foreach ($fields as $key => $field) {
                    if ($key == 0) {
                        $query->where($field, 'like', $searchText . '%');
                    } else {
                        $query->orWhere($field, 'like', $searchText . '%');
                    }
                }
            });
        }

        return $query;
    }

    /**
     * Searches for a given text in multiple fields of a related model.
     *
     * @param object $query The query object to be modified.
     * @param string $relation The name of the relation to be searched.
     * @param array $fields The fields to be searched.
     * @param string $searchText The text to search for.
     * @return object The modified query object.
     */
    public function searchToMultipleFieldsWithRelation($query, string $relation, array $fields, string $searchText)
    {
        if ($searchText) {
            $query->whereHas($relation, function ($query) use ($searchText, $fields) {
                $query->where(function ($query) use ($searchText, $fields) {
                    foreach ($fields as $key => $field) {
                        if ($key == 0) {
                            $query->where($field, 'like', $searchText . '%');
                        } else {
                            $query->orWhere($field, 'like', $searchText . '%');
                        }
                    }
                });
            });
        }

        return $query;
    }

    /**
     * Filter the query based on a relation's fields using the whereHas method and uses like
     *
     * @param object $query The query object to filter.
     * @param string $whereHasRelation The name of the relation to check for existence.
     * @param string $field The field on the relation to compare.
     * @param mixed $userStatus The value to compare the field against.
     * @return object The filtered query object.
     */
    public function filterWithWhereHasRelationLikeFields($query, $whereHasRelation, $field, $value)
    {
        if ($value == '' || $value == []) {
            return $query;
        }

        return $query->whereHas($whereHasRelation, function ($query) use ($field, $value) {
            $query->where($field, 'like', $value . '%');
        });
    }

    /**
     * Filter assets by age group.
     *
     * @param mixed $query The query object to filter
     * @param string $field The field on the model to compare
     * @param mixed $ageGroup The age group to filter by
     * @return mixed
     */
    public function filterAssetsByAgeGroup(mixed $query, string $field, mixed $ageGroup)
    {
        if (!$ageGroup) {
            return $query;
        }
        $ageGroupArray = explode('_', $ageGroup);
        $dateTo = $this->getDateFromAge($ageGroupArray[0]);
        $dateFrom = $this->getDateFromAge($ageGroupArray[1]);

        return $this->filterWithDirectDateRange($query, $field, $dateFrom, $dateTo);
    }

    /**
     * Get the date based on the provided age.
     *
     * @param mixed $age The age to calculate the date from
     * @return string The calculated date in the specified format, or an empty string if the age is 0
     */
    private function getDateFromAge($age)
    {
        return $age ? Carbon::now()->subYears($age)->format(config('date.formats.read_date_format')) : '';
    }

    /**
     * Filter the query based on the provided age and age unit.
     *
     * @param mixed $query The query object to filter
     * @param int $assetAge The age to filter by
     * @param string $ageUnit The unit of time to filter by (day, month, year)
     * @param string $field The field to filter by (default is 'assets.created_at')
     * @return mixed The filtered query object
     */
    public function applyAgeFilter($query, $assetAge, $ageUnit, $field = 'assets.created_at')
    {
        $date = match ($ageUnit) {
            'day' => Carbon::now()->subDays($assetAge),
            'month' => Carbon::now()->subMonths($assetAge),
            'year' => Carbon::now()->subYears($assetAge),
            default => null,
        };

        return $date ? $query->where($field, '<=', $date) : $query;
    }
}
