<?php

namespace App\Http\Controllers;

use App\Filters\ProjectFilters;
use App\Http\Helpers\FileUploader;
use App\Http\Helpers\UploadedFiles;
use App\Models\Engineer;
use App\Models\Firm;
use App\Models\Language;
use App\Models\LevelOfExperience;
use App\Models\Project;
use App\Models\ProjectFile;
use App\Models\Service;
use App\Models\Skill;
use App\Notifications\InvitedToProject;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Inertia\Inertia;
use Rinvex\Categories\Models\Category;

class ProjectController extends Controller
{

    public function __construct()
    {
        $this->middleware('project');
    }
    /**
     * Display a listing of the resource.
     *
     * @return Response
     */
    public function index(Request $request, ProjectFilters $filters)
    {
        $categories = Category::where("parent_id", null)
            ->with("descendants")
            ->get();

        $skills = Skill::where("verified", 1)->get();

        $projects = Project::filter($filters)
            ->where('status','=','Open')
            ->with(
                "files",
                "location",
                "skills",
                "user",
                "LevelOfExperience"
            )
            ->paginate(10);

        // get min and max price
        $minPrice = Project::min('amount');
        $maxPrice = Project::max('amount');
        if ($request->expectsJson()) {
            return response()->json($projects);
        }

        $saved = [];
        if (Auth::check()) {
            $saved = $request->user()->savedProjects();
        }

        // dd($saved);

        return Inertia::render("Projects/Index", [
            "categories" => $categories,
            "projects" => $projects,
            "skills" => $skills,
            "minPrice" => $minPrice,
            "maxPrice" => $maxPrice,
            "saved" => $saved,
        ]);
    }

    public function store(Request $request)
    {
        try {
            if($request->bidding_start!=null && $request->bidding_end!=null){
                $request->amount=$request->bidding_start;
            }
            $transaction = DB::transaction(function () use ($request) {
                $project = Project::create(
                    array_merge($this->validateProject($request, false), [
                        "user_id" => $request->user()->id,
                        "slug"=>Str::uuid()->toString(),
                    ])
                );
                $project->attachCategories($request->specialities);
                $project->skills()->attach($request->skills);
                $project->addLocation($request->location);
                $project->addMastery($request->language_id, 2);
                $project->addCurrency(1);
                if($request->file("files") != null &&
                    count($request->file("files")) > 0){
                    foreach ($request->file("files") as $file) {
                        ProjectFile::create([
                            "project_id" => $project->id,
                            'url' => FileUploader::store('projectsFiles/', $file),
                            'type' => FileUploader::getFileType($file),
                        ]);
                    }
                }
                $project->save();
                $project->searchable();
            });
            return is_null($transaction)
                ? back(303)->with(["success" => "success"])
                : $transaction;
        } catch (Exception $exception) {
            return redirect()->back()->withErrors(["error" => $exception->getMessage()])->with(["error" => $exception->getMessage()]);
        }
    }

    public function create()
    {
        return view("create-project");
    }

    public function draft(Request $request)
    {
        try {
            $transaction = DB::transaction(function () use ($request) {
                $project = Project::saveDraft($request);
            });
            return is_null($transaction)
                ? redirect()->route('client.projects.index')->with(["success" => "success"])
                : $transaction;
        } catch (Exception $exception) {
            return redirect()->back()->withErrors(["error" => $exception->getMessage()])->with(["error" => $exception->getMessage()]);
        }
    }

    public function rate(Request $request, $slug){
        $project= Project::where('slug',$slug)->firstOrFail();
         $project->addRate($request);
         return back(303)->with(["success" => "success"]);
    }

    /**
     * @param Request $request
     * @return array
     */
    public function validateProject(Request $request, bool $update): array
    {
        $request->validate([
            "files" => "array",
            "files.*" => $update? "nullable" :UploadedFiles::getRules($update),
            "skills" => ["required", "array"],
            "skills.*" => ["integer", Rule::exists("skills", "id")],
            "location.long" => "required",
            "location.lat" => "required",
            "location.address" => "string",
            "specialities" => ["array", "required"],
            "specialities.*" => [Rule::exists("categories", "id")],
            "language_id" => [Rule::exists("languages", "id"), "required"],
        ]);
        return $request->validate([
            "title" => "required",
            "description" => "required",
            "is_public" => "required",
            "level_of_experience_id" => [
                Rule::exists("level_of_experiences", "id"),
                "required",
            ],
            "start_date" => "date|nullable",
            "duration" => "int|required",
            "budget_type" => "required",
            "amount" => "nullable",
            "rate_type" => "nullable",
            "bidding_start" => "nullable",
            "bidding_end" => "nullable",
            "category_id" => [Rule::exists("categories", "id"), "required"],
        ]);
    }

    /**
     * Display the specified resource.
     *
     * @param Project $project
     * @return Response
     */
    public function show($slug)
    {
        //dd($slug);
//        dd($slug);
        $project= Project::where('slug',$slug)->firstOrFail();
        $languages = Language::all();
        $levelsOfExperience = LevelOfExperience::all();

        $project->load([
            "categories",
            "skills",
            "location",
            "files",
            'languageMasteries',
            // "proposals" => ["proposalable.user", "files", 'contract'],
            'contract',
            'user.location',
            ]
        );

        $project->loadCount("proposals");

        $project->user->loadCount('projects');

        $project->user->load('wallet');
        $categoriesIds=[];
        $skilIds=[];
        foreach ($project->categories as $cat){
            $categoriesIds[]=$cat->id;
        }
        foreach ($project->skills as $skill){
            $skilIds[]=$skill->id;
        }
        $project['language'] = $project->languageMasteries[0]['language'];
        $relatedProjects = Project::with(
            "files",
            "location",
            "skills",
            "user",
            "LevelOfExperience"
        )
            ->where("id", "!=", $project->id)
            ->where('status','=','Open')
            ->where("category_id", $project->category_id)
            ->orWhere('user_id',$project->user_id)
            ->orWhereHas('categories', function ($query) use ($categoriesIds) {
                return $query->whereIn('categories.id', $categoriesIds);
            })->
            orWhereHas('skills', function ($query) use ($skilIds) {
                return $query->whereIn('skills.id', $skilIds);
            })
            ->where("is_public", true)
            ->where("deleted_at", null)

            ->limit(2)
            ->get();
            $skills = Skill::where("verified", 1)->get();

            return Inertia::render("Projects/Single/Index", [
            "project" => $project,
            "languages" => $languages,
            "levelsOfExperience" => $levelsOfExperience,
            "relatedProjects" => $relatedProjects,
            "skills" => $skills,
        ]);
    }

    /**
     * Display the specified resource.
     *
     * @param Project $project
     * @return Response
     */
    public function toggleHidden($slug)
    {
        $project= Project::where('slug',$slug)->firstOrFail();
        $project->toggleHiddenStatus();
        return back(303)->with(["success" => "success"]);
    }

    public function inviteWorker($slug,Request $request)
    {

        $project= Project::where('slug',$slug)->firstOrFail();

        foreach ($request->workers as $worker){
            $entity = null;

            if($worker['type'] == 'engineers'){
                $entity = Engineer::find($worker['id']);
            }else if($worker['type'] == 'firms'){
                $entity = Firm::find($worker['id']);
            }
            if($entity && $entity->invitedProjects->where('id',$project->id)->first()==null)
            {
                $entity->invitedProjects()->attach([
                'project_id'=>$project->id
                ]);
                $entity->notify(new InvitedToProject($project));
            }

        }

        return back(303)->with(["success" => "success"]);
    }

    public function save($slug){
        $project = Project::where('slug',$slug)->firstOrFail();
        Auth::user()?->toggleLike($project);
        return back(303)->with(["success" => "success"]);
    }
    /**
     * Show the form for editing the specified resource.
     *
     * @param Project $project
     * @return Response
     */
    public function edit(Project $project)
    {
        //
    }

    public function update(Request $request, $slug)
    {

        if($request->bidding_start!=null && $request->bidding_end!=null){
                $request->amount= intval($request->bidding_start);
            }
        $project= Project::where('slug',$slug)->first()?? Project::getAllDrafts()->where('slug',$slug)->firstOrFail();
        if($request->user()->id != $project->user_id){
            session()->flash('error', "You are not authorized to perform this action");
            return redirect( url()->previous());
        }
        try {
            $transaction = DB::transaction(function () use (
                $request,
                $project
            ) {
                if ($project->draft!=null){
                    $new= Project::create($this->validateProject($request,false));
                    $new->slug = $project->slug;
                    $new->user_id = Auth::user()->id;
                    $new->save();
                    $project->delete();
                    $project->draft->delete();
                    $project=$new;
                    $project->addCurrency(1);
                }else{
                    $project->update($this->validateProject($request, true));
                }
                $project->categories()->sync($request->specialities);
                $project->skills()->sync($request->skills);
                $project->addLocation($request->location);
                if($request->language_id!=null){
                    $project->addMastery($request->language_id, 2);
                }
                foreach ($project->files()->get() as $item) {
                    if (
                        !in_array(
                            $item->id,
                            array_column($request->file("files")??[], "id")
                        )
                    ) {
                        $item->delete();
                    }
                }
                if (
                    $request->file("files") != null &&
                    count($request->file("files")) > 0
                ) {
                    foreach ($request->file("files") as $item) {
                        ProjectFile::create([
                            "project_id" => $project->id,
                            "url" => FileUploader::store(
                                "projectsFiles/",
                                $item
                            ),
                        ]);
                    }
                }
                $project->searchable();
            });

            return is_null($transaction)
                ? back(303)->with(["success" => "success"])
                : $transaction;
        } catch (Exception $exception) {

            return redirect()->back()->withErrors(["error" => $exception->getMessage()])->with(["error" => $exception->getMessage()]);
        }
    }

    public function destroy($slug)
    {
        $project = Project::where('slug', $slug)->first() ?? Project::getAllDrafts()->where('slug', $slug)->firstOrFail();
        if (Auth::user()->id != $project->user_id) {
            session()->flash('error', "You are not authorized to perform this action");
            return redirect(url()->previous());
        }
        if ($project->status == "Processing") {
            return back(303)->with([
                "error" => "Can not delete Project While Processing",
            ]);
        } else {
            try {
                if ($project->draft != null) {
                    $project->draft->delete();
                }
                $project->delete();
                return redirect()->route('client.projects.index')->with(["success" => "success"]);

            }catch( Exception $exception) {
                dd($exception);
            }
        }
    }
}
