<?php

namespace Binaryk\LaravelRestify\Fields;

use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Binaryk\LaravelRestify\MCP\Requests\McpRequestable;
use Binaryk\LaravelRestify\Repositories\Repository;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;

/**
 * @template TKey of array-key
 * @template TValue
 *
 * @extends \Illuminate\Support\Collection<TKey, TValue>
 */
class FieldCollection extends Collection
{
    public function authorized(Request $request): self
    {
        return $this->filter(function (OrganicField $field) use ($request) {
            return $field->authorize($request);
        })->values();
    }

    public function authorizedUpdate(Request $request): self
    {
        return $this->filter(function (OrganicField $field) use ($request) {
            return $field->authorizedToUpdate($request);
        })->values();
    }

    public function authorizedPatch(Request $request): self
    {
        return $this->filter(function (OrganicField $field) use ($request) {
            return $field->authorizedToPatch($request);
        })->values();
    }

    public function authorizedUpdateBulk(Request $request): self
    {
        return $this->filter(function (OrganicField $field) use ($request) {
            return $field->authorizedToUpdateBulk($request);
        })->values();
    }

    public function authorizedStore(Request $request): self
    {
        return $this->filter(function (OrganicField $field) use ($request) {
            return $field->authorizedToStore($request);
        })->values();
    }

    public function resolve($repository): self
    {
        return $this->each(function ($field) use ($repository) {
            $field->resolve($repository);
        });
    }

    public function forIndex(RestifyRequest $request, $repository): self
    {
        return $this
            ->filter(fn (Field $field) => ! $field instanceof EagerField)
            ->filter(function (Field $field) use ($repository, $request) {
                return $field->isShownOnIndex($request, $repository);
            })->values();
    }

    public function forMcpIndex(RestifyRequest $request, $repository): self
    {
        // If this is an MCP request and repository has fieldsForMcpIndex method
        if ($request instanceof McpRequestable && method_exists($repository, 'fieldsForMcpIndex')) {
            // Get the MCP-specific fields from the repository
            $mcpFields = $repository->fieldsForMcpIndex($request);
            $mcpFieldAttributes = collect($mcpFields)->map(fn ($field) => $field->attribute)->toArray();

            // Filter the current collection to only include MCP fields
            return $this
                ->filter(fn (Field $field) => ! $field instanceof EagerField)
                ->filter(fn (Field $field) => in_array($field->attribute, $mcpFieldAttributes))
                ->filter(function (Field $field) use ($repository, $request) {
                    return $field->isShownOnMcp($request, $repository);
                })->values();
        }

        // Fallback to regular index filtering for non-MCP requests
        return $this->forIndex($request, $repository);
    }

    public function forShow(RestifyRequest $request, $repository): self
    {
        return $this
            ->filter(fn (Field $field) => ! $field instanceof EagerField)
            ->filter(function (Field $field) use ($repository, $request) {
                return $field->isShownOnShow($request, $repository);
            })->values();
    }

    public function forStore(RestifyRequest $request, $repository): self
    {
        return $this
            ->filter(fn (Field $field) => ! $field instanceof EagerField)
            ->filter(function (Field $field) use ($repository, $request) {
                return $field->isShownOnStore($request, $repository);
            })->values();
    }

    public function withActions(RestifyRequest $request, $repository, $row = null): self
    {
        return $this
            ->inRequest($request, $row)
            ->filter(fn (Field $field) => $field->isActionable())
            ->values();
    }

    public function withoutActions(RestifyRequest $request, $repository): self
    {
        return $this
            ->reject(fn (Field $field) => $field->isActionable() && $field->actionHandler?->skipFieldFill($request))
            ->values();
    }

    public function forStoreBulk(RestifyRequest $request, $repository): self
    {
        return $this->filter(function (Field $field) use ($repository, $request) {
            return $field->isShownOnStoreBulk($request, $repository);
        })->values();
    }

    public function forUpdate(RestifyRequest $request, $repository): self
    {
        return $this
            ->filter(fn (Field $field) => ! $field instanceof EagerField)
            ->filter(function (Field $field) use ($repository, $request) {
                return $field->isShownOnUpdate($request, $repository);
            })->values();
    }

    public function forUpdateBulk(RestifyRequest $request, $repository): self
    {
        return $this->filter(function (Field $field) use ($repository, $request) {
            return $field->isShownOnUpdateBulk($request, $repository);
        })->values();
    }

    public function filterForManyToManyRelations(RestifyRequest $request): self
    {
        return $this->filter(function ($field) {
            return $field instanceof BelongsToMany || $field instanceof MorphToMany;
        })->filter(fn (EagerField $field) => $field->authorize($request));
    }

    public function forEager(RestifyRequest $request, Repository $repository): self
    {
        return $this
            ->filter(fn (Field $field) => $field instanceof EagerField)
            ->filter(fn (Field $field) => $field->authorize($request))
            ->unique();
    }

    public function forBelongsTo(RestifyRequest $request): self
    {
        return $this
            ->filter(fn (Field $field) => $field instanceof BelongsTo)
            ->unique();
    }

    public function setRepository(Repository $repository): self
    {
        return $this->each(fn (Field $field) => $field->setRepository($repository));
    }

    public function findFieldByAttribute($attribute, $default = null)
    {
        foreach ($this->items as $field) {
            if (isset($field->attribute) && $field->attribute === $attribute) {
                return $field;
            }
        }

        return null;
    }

    public function inRequest(RestifyRequest $request, $row = null): self
    {
        return $this
            ->filter(
                fn (Field $field) => $request->hasAny($field->attribute, $row.'.'.$field->attribute)
                    || $request->hasFile($field->attribute)
            )
            ->values();
    }

    public function inList(array $columns = []): self
    {
        return $this
            ->filter(fn (Field $field) => in_array($field->getAttribute(), $columns, true))
            ->values();
    }

    public function areFiles(): self
    {
        return $this
            ->filter(fn (Field $field) => $field instanceof File)
            ->values();
    }
}
