<?php
namespace App\Http\Controllers;

use App\Models\Account;
use App\Models\Activity;
use App\Models\Deal;
use App\Models\DealStage;
use App\Models\Product;
use App\Models\Project;
use App\Models\ProjectMilestone;
use App\Models\ProjectTask;
use App\Models\User;
use App\Services\CrmNotificationService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\View\View;

class ProjectController extends Controller
{
    public function __construct()
    {
        $this->middleware(function($request,$next){
            $action=$request->route()?->getActionMethod();
            $permission=match($action){
                'store','convertDeal'=>'create_projects','destroy'=>'delete_projects',
                'index','create','show'=>'view_projects',default=>'update_projects',
            };
            abort_unless($request->user()->is_admin || $request->user()->can($permission),403);
            return $next($request);
        });
    }

    public function index(Request $request): View {
        $this->useDefaultDateRange($request);
        $request->validate([
            'q'=>['nullable','string','max:100'],
            'status'=>['nullable',Rule::in(['planned','active','on_hold','completed','cancelled'])],
            'date_from'=>['nullable','date'],'date_to'=>['nullable','date','after_or_equal:date_from'],
        ]);
        $search=trim((string)$request->input('q'));
        $projects=Project::with(['deal','account','owner','assignees'])->withCount(['milestones','tasks'])
            ->when(!$request->user()->is_admin,fn($q)=>$q->where(fn($visible)=>$visible
                ->where('owner_id',$request->user()->id)
                ->orWhereHas('assignments',fn($assignment)=>$assignment->where('user_id',$request->user()->id))))
            ->when($request->filled('status'),fn($q)=>$q->where('status',$request->status))
            ->when($request->filled('date_from'),fn($q)=>$q->whereDate('created_at','>=',$request->date_from))
            ->when($request->filled('date_to'),fn($q)=>$q->whereDate('created_at','<=',$request->date_to))
            ->when($search!=='',fn($q)=>$q->where(fn($filter)=>$filter
                ->where('name','like',"%{$search}%")
                ->orWhere('description','like',"%{$search}%")))
            ->latest()->paginate(20)->withQueryString();
        return view('projects.projects',compact('projects'));
    }

    public function create(): View {
        $businessId=auth()->user()->business_id;
        $deals=collect();
        $accounts=collect();
        $users=User::where('business_id',$businessId)->whereKey(auth()->id())->get();
        return view('projects.projects-create',compact('deals','accounts','users'));
    }

    public function store(Request $request): RedirectResponse {
        $data=$this->validateProject($request); unset($data['assignee_ids'],$data['status_reason']); $data['business_id']=$request->user()->business_id;
        $data['owner_id']??=$request->user()->id;
        $project=Project::create($data);
        $this->syncAssignments($project,$request->input('assignee_ids',[]));
        $this->history($project,null,$project->status,'Project created');
        return redirect()->route('projects-view',['id'=>$project->id])->with('success','Project created.');
    }

    public function show(Request $request): View {
        $project=Project::with(['deal.accounts','account','owner.reportingManager','milestones.tasks.assignee','tasks.assignee',
            'products','meetings.attendees','assignees.reportingManager','statusHistory.changedBy'])->findOrFail($request->integer('id'));
        $this->authorizeBusiness($project);
        $this->authorizeVisible($project);
        $businessId=$request->user()->business_id;
        $products=collect();
        $userIds=$project->tasks->pluck('assigned_to')->merge($project->assignees->pluck('id'))
            ->merge($project->meetings->flatMap(fn($meeting)=>$meeting->attendees->pluck('id')))
            ->push($project->owner_id)->filter()->unique();
        $users=User::where('business_id',$businessId)->whereIn('id',$userIds)->with('reportingManager')->orderBy('name')->get();
        $employeeReport=$project->tasks->groupBy('assigned_to')->map(fn($tasks)=>[
            'user'=>$tasks->first()->assignee,'total'=>$tasks->count(),
            'completed'=>$tasks->where('status','completed')->count(),
            'overdue'=>$tasks->filter(fn($t)=>$t->due_at && $t->due_at->isPast() && $t->status!=='completed')->count(),
        ]);
        return view('projects.projects-view',compact('project','products','users','employeeReport'));
    }

    public function update(Request $request, Project $project): RedirectResponse {
        $this->authorizeBusiness($project); $old=$project->status;
        $data=$this->validateProject($request,$project); unset($data['assignee_ids'],$data['status_reason']); $project->update($data);
        $this->syncAssignments($project,$request->input('assignee_ids',$project->assignees->pluck('id')->all()));
        if($old!==$project->status)$this->history($project,$old,$project->status,$request->input('status_reason'));
        return back()->with('success','Project updated.');
    }

    public function destroy(Project $project): RedirectResponse {
        $this->authorizeBusiness($project); $project->delete();
        return redirect()->route('projects')->with('success','Project moved to deleted records.');
    }

    public function convertDeal(Deal $deal): RedirectResponse {
        abort_unless($deal->business_id===auth()->user()->business_id,403);
        abort_unless(DealStage::whereKey($deal->stage_id)->where('name','Closed Won')->exists(),422,'Only Closed Won Deals can become Projects.');
        $project=Project::firstOrCreate(['business_id'=>$deal->business_id,'deal_id'=>$deal->id],[
            'account_id'=>$deal->account_id ?: $deal->accounts()->value('accounts.id'),'owner_id'=>$deal->owner_id,
            'name'=>$deal->name.' Implementation','description'=>'Created from Closed Won Deal '.$deal->name,
            'status'=>'planned','start_date'=>now()->toDateString(),'due_date'=>$deal->closing_date,'progress'=>0,
        ]);
        $this->syncAssignments($project,array_filter([$deal->owner_id]));
        if($project->wasRecentlyCreated)$this->history($project,null,'planned','Converted from Closed Won Deal');
        return redirect()->route('projects-view',['id'=>$project->id])->with('success','Deal converted to Project.');
    }

    public function milestoneStore(Request $request, Project $project): RedirectResponse {
        $this->authorizeBusiness($project); $data=$request->validate(['name'=>'required|max:255','description'=>'nullable|string','due_date'=>'nullable|date','baseline_date'=>'nullable|date','depends_on_milestone_id'=>'nullable|integer','blocking_reason'=>'nullable|string|max:2000','status'=>'required|in:pending,in_progress,completed,cancelled']);if(!empty($data['depends_on_milestone_id']))abort_unless($project->milestones()->whereKey($data['depends_on_milestone_id'])->exists(),422);
        $project->milestones()->create($data); $this->history($project,$project->status,$project->status,'Milestone created: '.$data['name']); return back()->with('success','Milestone created.');
    }
    public function milestoneUpdate(Request $request, Project $project, ProjectMilestone $milestone): RedirectResponse {
        $this->authorizeChild($project,$milestone); $data=$request->validate(['name'=>'required|max:255','description'=>'nullable|string','due_date'=>'nullable|date','baseline_date'=>'nullable|date','depends_on_milestone_id'=>'nullable|integer','blocking_reason'=>'nullable|string|max:2000','status'=>'required|in:pending,in_progress,completed,cancelled']);if(!empty($data['depends_on_milestone_id']))abort_unless($data['depends_on_milestone_id']!=$milestone->id&&$project->milestones()->whereKey($data['depends_on_milestone_id'])->exists(),422);if($data['status']==='completed'&&$data['depends_on_milestone_id']&&$project->milestones()->whereKey($data['depends_on_milestone_id'])->where('status','!=','completed')->exists())abort(422,'Complete the dependent milestone first.');if($data['status']==='completed')$data['actual_date']=now()->toDateString();
        $milestone->update($data); return back()->with('success','Milestone updated.');
    }
    public function milestoneDestroy(Project $project, ProjectMilestone $milestone): RedirectResponse {
        $this->authorizeChild($project,$milestone); $milestone->delete(); return back()->with('success','Milestone deleted.');
    }
    public function riskIssueStore(Request $request, Project $project): RedirectResponse {
        $this->authorizeBusiness($project);$data=$request->validate(['type'=>'required|in:risk,issue','title'=>'required|max:255','description'=>'nullable|string|max:5000','owner_id'=>['required','integer',Rule::exists('users','id')->where(fn($q)=>$q->where('business_id',$project->business_id))],'severity'=>'required|in:low,medium,high,critical','probability'=>'required|in:low,medium,high','mitigation'=>'nullable|string|max:5000','due_date'=>'nullable|date','status'=>'required|in:open,mitigating,resolved,closed']);$id=\DB::table('project_risks_issues')->insertGetId($data+['business_id'=>$project->business_id,'project_id'=>$project->id,'created_at'=>now(),'updated_at'=>now()]);if(in_array($data['severity'],['high','critical'],true)){ $leaders=User::where('business_id',$project->business_id)->whereIn('id',array_filter([$project->owner_id,$project->project_manager_id]))->get();app(\App\Services\CrmNotificationService::class)->send($leaders,'project_risk_alert','High-severity project '.$data['type'],$project->name.': '.$data['title'],route('projects-view',['id'=>$project->id]),'Project',$project->id,'open');}return back()->with('success','Project '.$data['type'].' recorded.');
    }
    public function riskIssueUpdate(Request $request, Project $project, int $item): RedirectResponse { $this->authorizeBusiness($project);$data=$request->validate(['status'=>'required|in:open,mitigating,resolved,closed','mitigation'=>'nullable|string|max:5000']);\DB::table('project_risks_issues')->where('id',$item)->where('project_id',$project->id)->where('business_id',$project->business_id)->update($data+['updated_at'=>now()]);return back()->with('success','Risk/issue updated.'); }
    public function taskStore(Request $request, Project $project): RedirectResponse {
        $this->authorizeBusiness($project); $data=$this->validateTask($request,$project); $project->tasks()->create($data);
        $this->history($project,$project->status,$project->status,'Task created: '.$data['title']); return back()->with('success','Project task created.');
    }
    public function taskUpdate(Request $request, Project $project, ProjectTask $task): RedirectResponse {
        $this->authorizeChild($project,$task); $task->update($this->validateTask($request,$project)); return back()->with('success','Project task updated.');
    }
    public function taskDestroy(Project $project, ProjectTask $task): RedirectResponse {
        $this->authorizeChild($project,$task); $task->delete(); return back()->with('success','Project task deleted.');
    }
    public function productStore(Request $request, Project $project): RedirectResponse {
        $this->authorizeBusiness($project); $data=$request->validate(['product_id'=>'required|integer|exists:products,id','quantity'=>'required|numeric|min:0.001','notes'=>'nullable|string']);
        abort_unless(Product::whereKey($data['product_id'])->where('business_id',$project->business_id)->exists(),403);
        $project->products()->syncWithoutDetaching([$data['product_id']=>['quantity'=>$data['quantity'],'notes'=>$data['notes']??null]]);
        $this->history($project,$project->status,$project->status,'Product linked: '.Product::find($data['product_id'])?->product_name);
        return back()->with('success','Product linked.');
    }
    public function productDestroy(Project $project, Product $product): RedirectResponse {
        $this->authorizeBusiness($project); $project->products()->detach($product->id); return back()->with('success','Product removed.');
    }
    public function assign(Request $request, Project $project): RedirectResponse {
        $this->authorizeBusiness($project); $data=$request->validate(['assignee_ids'=>'nullable|array','assignee_ids.*'=>'integer|exists:users,id']);
        $this->syncAssignments($project,$data['assignee_ids']??[]);
        $this->history($project,$project->status,$project->status,'Project team updated'); return back()->with('success','Project team updated.');
    }
    public function meetingStore(Request $request, Project $project): RedirectResponse {
        $this->authorizeBusiness($project); $data=$request->validate(['title'=>'required|max:255','meeting_type'=>'required|in:online,physical,phone','location'=>'nullable|max:255','agenda'=>'nullable|string','start_date'=>'required|date','start_time'=>'required','end_date'=>'nullable|date','end_time'=>'nullable','attendee_ids'=>'nullable|array','attendee_ids.*'=>'integer|exists:users,id']);
        $attendees=$data['attendee_ids']??[]; unset($data['attendee_ids']);
        $activity=$project->activities()->create($data+['business_id'=>$project->business_id,'activity_type_id'=>2,'owner_id'=>auth()->id(),'lifecycle_status'=>'planned','status'=>'planned']);
        $activity->attendees()->sync($attendees);
        $this->history($project,$project->status,$project->status,'Meeting scheduled: '.$activity->title); return back()->with('success','Project meeting scheduled.');
    }

    private function validateProject(Request $request,?Project $project=null): array {
        return $request->validate(['name'=>'required|max:255','description'=>'nullable|string','deal_id'=>'nullable|integer|exists:deals,id','account_id'=>'nullable|integer|exists:accounts,id','owner_id'=>'nullable|integer|exists:users,id','project_manager_id'=>'nullable|integer|exists:users,id','project_type'=>'nullable|string|max:255','priority'=>['nullable',Rule::in(['low','medium','high','urgent'])],'status'=>['required',Rule::in(['planned','active','on_hold','completed','cancelled'])],'start_date'=>'nullable|date','due_date'=>'nullable|date|after_or_equal:start_date','actual_end_date'=>'nullable|date|after_or_equal:start_date','progress'=>'required|integer|min:0|max:100','budget'=>'nullable|numeric|min:0','contract_value'=>'nullable|numeric|min:0','billing_type'=>'nullable|string|max:100','technology_stack'=>'nullable|string','scope_of_work'=>'nullable|string','requirements'=>'nullable|string','deliverables'=>'nullable|string','risks'=>'nullable|string','status_reason'=>'nullable|string','assignee_ids'=>'nullable|array','assignee_ids.*'=>'integer|exists:users,id']);
    }
    private function validateTask(Request $request,Project $project): array {
        $data=$request->validate(['milestone_id'=>'nullable|integer|exists:project_milestones,id','assigned_to'=>'nullable|integer|exists:users,id','title'=>'required|max:255','description'=>'nullable|string','priority'=>'required|in:low,medium,high,urgent','status'=>'required|in:pending,in_progress,completed,cancelled','due_at'=>'nullable|date']);
        if(!empty($data['milestone_id']))abort_unless($project->milestones()->whereKey($data['milestone_id'])->exists(),422);
        return $data;
    }
    private function syncAssignments(Project $project,array $ids): void {
        $valid=User::where('business_id',$project->business_id)->whereIn('id',$ids)->pluck('id');
        $existing=$project->assignments()->pluck('user_id');
        $project->assignments()->whereNotIn('user_id',$valid)->delete();
        foreach($valid as $id){
            $project->assignments()->firstOrCreate(['business_id'=>$project->business_id,'user_id'=>$id],['assigned_by'=>auth()->id()]);
            if(!$existing->contains($id) && ($user=User::find($id))){
                app(CrmNotificationService::class)->assignment($user,$project,'Project',route('projects-view',['id'=>$project->id]));
            }
        }
    }
    private function history(Project $project,?string $from,string $to,?string $reason): void {
        $project->statusHistory()->create(['business_id'=>$project->business_id,'from_status'=>$from,'to_status'=>$to,'reason'=>$reason,'changed_by'=>auth()->id(),'changed_at'=>now()]);
    }
    private function authorizeBusiness(Project $project): void {abort_unless($project->business_id===auth()->user()->business_id,403);}
    private function authorizeVisible(Project $project): void {
        $user=auth()->user();
        abort_unless($user->is_admin || (int)$project->owner_id===(int)$user->id ||
            $project->assignments()->where('user_id',$user->id)->exists(),403);
    }
    private function authorizeChild(Project $project,$child): void {$this->authorizeBusiness($project);abort_unless((int)$child->project_id===(int)$project->id,404);}
}
