<?php

namespace App\Http\Controllers;

use App\Models\Account;
use App\Models\BillingFrequency;
use App\Models\Campaign;
use App\Models\Contact;
use App\Models\ContactEmail;
use App\Models\ContactLanguage;
use App\Models\ContactPhoneNum;
use App\Models\ContactSocialMedia;
use App\Models\Deal;
use App\Models\DealProductLine;
use App\Models\Language;
use App\Models\Lead;
use App\Models\Priority;
use App\Models\User;
use App\Models\UserAccess;
use App\Models\AccessType;
use App\Models\Activity;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use App\Models\CampaignTarget;
use App\Models\CampaignType;
use App\Models\Ticket;

class CampaignController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');

        $this->middleware('permission:list_campaigns')->only(['index']);
        $this->middleware('permission:list_campaigns')->only(['show']);
        $this->middleware('permission:create_campaigns')->only(['create', 'store']);
        $this->middleware('permission:update_campaigns')->only(['edit', 'update']);
        $this->middleware('permission:delete_campaigns')->only(['destroy']);
    }

    public function templateTypes()
    {
        abort_unless(auth()->user()->can('list_campaigns'), 403);
        $types = CampaignType::orderBy('name')->get();
        return view('campaigns.template-types', compact('types'));
    }

    public function storeTemplateType(Request $request)
    {
        abort_unless(auth()->user()->can('create_campaigns'), 403);
        $data = $request->validate(['name' => ['required','string','max:100','unique:campaign_types,name']]);
        CampaignType::create($data);
        return back()->with('success', 'Campaign template type created.');
    }

    public function updateTemplateType(Request $request, CampaignType $campaignType)
    {
        abort_unless(auth()->user()->can('update_campaigns'), 403);
        $data = $request->validate(['name' => ['required','string','max:100','unique:campaign_types,name,'.$campaignType->id]]);
        $campaignType->update($data);
        return back()->with('success', 'Campaign template type updated.');
    }

    public function destroyTemplateType(CampaignType $campaignType)
    {
        abort_unless(auth()->user()->can('delete_campaigns'), 403);
        abort_if(Campaign::where('campaign_type_id', $campaignType->id)->exists(), 422, 'This type is used by a campaign.');
        $campaignType->delete();
        return back()->with('success', 'Campaign template type deleted.');
    }
    
    public function index(Request $request)
    {

        $user = auth()->user();
        $this->useDefaultDateRange($request);

        // Get campaigns based on user access permissions
        $campaignQuery = $this->getAccessibleCampaigns($user->id);
        $request->validate([
            'q'=>['nullable','string','max:100'],
            'status'=>['nullable',Rule::in(['Proposed','Ready to Launch','Launched','Completed','Cancelled','Suspended'])],
            'date_from'=>['nullable','date'],'date_to'=>['nullable','date','after_or_equal:date_from'],
        ]);
        $search = trim((string) $request->input('q'));
        $campaignQuery
        ->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 ($query) => $query->where(fn ($filter) => $filter
            ->where('campaign_name', 'like', "%{$search}%")
            ->orWhere('campaign_code', 'like', "%{$search}%")
            ->orWhere('lead_source', 'like', "%{$search}%")));
        $totalCount = (clone $campaignQuery)->count();
        $campaignPaginator = $campaignQuery->latest()->paginate(25)->withQueryString();
        $campaigns = collect($campaignPaginator->items());

        // Business ID filter
        $businessId = $user->business_id;

        // Accessible user IDs
        $accessibleUserIds = $this->getAccessibleUserIds($user->id);

        // Today’s campaigns count
        $todayCount = Campaign::whereIn('owner_id', $accessibleUserIds)
            ->where('business_id', $businessId)
            ->whereDate('created_at', today())
            ->count();

        // This month’s campaigns count
        $monthCount = Campaign::whereIn('owner_id', $accessibleUserIds)
            ->where('business_id', $businessId)
            ->whereMonth('created_at', now()->month)
            ->whereYear('created_at', now()->year)
            ->count();

        // This year’s campaigns count
        $yearCount = Campaign::whereIn('owner_id', $accessibleUserIds)
            ->where('business_id', $businessId)
            ->whereYear('created_at', now()->year)
            ->count();

        // Total campaigns count
        return view('campaigns.campaigns', compact(
            'campaigns',
            'todayCount',
            'monthCount',
            'yearCount',
            'totalCount',
            'campaignPaginator'
        ));
    }

    public function dashboard(Request $request)
    {
        $this->useDefaultDateRange($request);
        $data=$request->validate(['campaign_type_id'=>'nullable|integer','owner_id'=>'nullable|integer','status'=>'nullable|string|max:100','lead_source'=>'nullable|string|max:255','date_from'=>'nullable|date','date_to'=>'nullable|date|after_or_equal:date_from']);
        $businessId=$request->user()->business_id;
        $campaigns=Campaign::where('business_id',$businessId)
            ->when($data['campaign_type_id']??null,fn($q,$v)=>$q->where('campaign_type_id',$v))
            ->when($data['owner_id']??null,fn($q,$v)=>$q->where('owner_id',$v))
            ->when($data['status']??null,fn($q,$v)=>$q->where('status',$v))
            ->when($data['lead_source']??null,fn($q,$v)=>$q->where('lead_source',$v))
            ->when($data['date_from']??null,fn($q,$v)=>$q->whereDate('created_at','>=',$v))
            ->when($data['date_to']??null,fn($q,$v)=>$q->whereDate('created_at','<=',$v))
            ->withCount(['targets as target_count','targets as sent_count'=>fn($q)=>$q->whereNotNull('sent_at'),'targets as delivered_count'=>fn($q)=>$q->where('delivery_status','delivered'),'targets as failed_count'=>fn($q)=>$q->where('delivery_status','failed'),'targets as responded_count'=>fn($q)=>$q->whereNotNull('responded_at'),'targets as conversion_count'=>fn($q)=>$q->where('converted',true)])
            ->withSum('targets as conversion_value','conversion_value')->latest()->get();
        $ids=$campaigns->pluck('id');
        $ticketCounts=Ticket::where('business_id',$businessId)->whereIn('campaign_id',$ids)->selectRaw('campaign_id, COUNT(*) total')->groupBy('campaign_id')->pluck('total','campaign_id');
        $activityCounts=Activity::where('business_id',$businessId)->whereIn('related_campaign_id',$ids)->selectRaw('related_campaign_id, COUNT(*) total')->groupBy('related_campaign_id')->pluck('total','related_campaign_id');
        $campaigns->each(function($campaign)use($ticketCounts,$activityCounts){$campaign->ticket_count=$ticketCounts[$campaign->id]??0;$campaign->activity_count=$activityCounts[$campaign->id]??0;$campaign->roi=(float)$campaign->actual_cost>0?round(((float)$campaign->actual_revenue-(float)$campaign->actual_cost)/(float)$campaign->actual_cost*100,1):0;});
        $targets = $campaigns->sum('target_count');
        $delivered = $campaigns->sum('delivered_count');
        $responses = $campaigns->sum('responded_count');
        $conversions = $campaigns->sum('conversion_count');
        $summary=['campaigns'=>$campaigns->count(),'targets'=>$targets,'delivered'=>$delivered,'tickets'=>$ticketCounts->sum(),'budget'=>$campaigns->sum('budgeted_cost'),'actual_cost'=>$campaigns->sum('actual_cost'),'expected_revenue'=>$campaigns->sum('expected_revenue'),'actual_revenue'=>$campaigns->sum('actual_revenue'),'responses'=>$responses,'conversions'=>$conversions,'conversion_value'=>$campaigns->sum('conversion_value'),'delivery_rate'=>$targets>0?round($delivered/$targets*100,1):0,'response_rate'=>$targets>0?round($responses/$targets*100,1):0,'conversion_rate'=>$targets>0?round($conversions/$targets*100,1):0,'roi'=>$campaigns->sum('actual_cost')>0?round(($campaigns->sum('actual_revenue')-$campaigns->sum('actual_cost'))/$campaigns->sum('actual_cost')*100,1):0];
        $filters=['types'=>CampaignType::orderBy('name')->get(),'owners'=>User::where('business_id',$businessId)->orderBy('name')->get(['id','name']),'statuses'=>Campaign::where('business_id',$businessId)->distinct()->pluck('status')->filter(),'sources'=>Campaign::where('business_id',$businessId)->distinct()->pluck('lead_source')->filter()];
        return view('campaigns.dashboard',compact('campaigns','summary','filters'));
    }


    private function getAccessibleCampaigns($userId)
    {
        $accessibleUserIds = $this->getAccessibleUserIds($userId);
        
        return Campaign::whereIn('owner_id', $accessibleUserIds)
            ->where('business_id', auth()->user()->business_id);
    }


    // private function getAccessibleUserIds($currentUserId)
    // {
    //     $accessibleUserIds = collect([$currentUserId]); // Always include self

    //     // Get users with PUBLIC access (anyone can access them)
    //     $publicUsers = UserAccess::where('access_type_id', AccessType::PUBLIC)
    //         ->pluck('user_id');
    //     $accessibleUserIds = $accessibleUserIds->merge($publicUsers);

    //     // Get users with SPECIFIC_USERS access where current user is specifically allowed
    //     $specificUsers = UserAccess::where('access_type_id', AccessType::SPECIFIC_USERS)
    //         ->where('specific_user_id', $currentUserId)
    //         ->pluck('user_id');
    //     $accessibleUserIds = $accessibleUserIds->merge($specificUsers);

    //     // PRIVATE users are NOT included (except self, which is already included above)
        
    //     return $accessibleUserIds->unique()->toArray();
    // }

    private function getAccessibleUserIds($currentUserId)
    {
        $accessibleUserIds = collect([$currentUserId]); // Always include self

        // Check if current user is admin - admins can see all users
        $currentUser = User::find($currentUserId);
        if ($currentUser && $currentUser->is_admin === 1) {
            // Return all user IDs for admin users
            return User::where('business_id', auth()->user()->business_id)
                ->pluck('id')
                ->toArray();
        }

        // First, check what access type the current user has
        $currentUserAccess = UserAccess::where('user_id', $currentUserId)->first();
        
        // If current user is PRIVATE, they can only see their own data
        if ($currentUserAccess && $currentUserAccess->access_type_id == AccessType::PRIVATE) {
            return $accessibleUserIds->unique()->toArray(); // Return only self
        }

        // If current user is PUBLIC, they can see PUBLIC, PRIVATE, and SPECIFIC users
        if ($currentUserAccess && $currentUserAccess->access_type_id == AccessType::PUBLIC) {
            $publicUsers = UserAccess::where('access_type_id', AccessType::PUBLIC)
                ->pluck('user_id');
            $privateUsers = UserAccess::where('access_type_id', AccessType::PRIVATE)
                ->pluck('user_id');
            $specificUsers = UserAccess::where('access_type_id', AccessType::SPECIFIC_USERS)
                ->pluck('user_id');
            
            $accessibleUserIds = $accessibleUserIds->merge($publicUsers)
                ->merge($privateUsers)
                ->merge($specificUsers);
        return $accessibleUserIds->unique()->toArray();
        } 
        // else {
        //     // For SPECIFIC users, they can see PUBLIC users only
        //     $publicUsers = UserAccess::where('access_type_id', AccessType::PUBLIC)
        //         ->pluck('user_id');
        //     $accessibleUserIds = $accessibleUserIds->merge($publicUsers);

        //     return $accessibleUserIds->unique()->toArray();
        // }

        // Get users with SPECIFIC_USERS access where current user is specifically allowed
        $specificUsers = UserAccess::where('access_type_id', AccessType::SPECIFIC_USERS)
            ->where('user_id', $currentUserId)
            ->pluck('specific_user_id'); 
        $accessibleUserIds = $accessibleUserIds->merge($specificUsers);

        return $accessibleUserIds->unique()->toArray();
    }

    public function create()
    {
        $languages = Language::all();
        $contacts = Contact::all();
        $accounts = Account::all();
        $leads = Lead::all();
        $priorities = Priority::all();
        $billing_frequencies = BillingFrequency::all();
        $users = $this->getAccessibleUsers();

        return view('campaigns.campaign-create', compact('languages', 'contacts', 'accounts', 'leads', 'priorities', 'billing_frequencies', 'users'));
    }

    private function getAccessibleUsers()
    {
        $accessibleUserIds = $this->getAccessibleUserIds(auth()->id());
        return User::whereIn('id', $accessibleUserIds)->get();
    }

    public function store(Request $request)
    {
        // Validate that the selected owner is accessible to current user
        $accessibleUserIds = $this->getAccessibleUserIds(auth()->id());
        
        $request->validate([
            'owner' => 'required|in:' . implode(',', $accessibleUserIds),
            'actual_revenue' => 'nullable|numeric|min:0',
            'lead_source' => 'nullable|string|max:255',
            'lead_ids' => 'nullable|array', 'contact_ids' => 'nullable|array', 'customer_ids' => 'nullable|array',
            'lead_ids.*' => [Rule::exists('leads','id')->where(fn($q)=>$q->where('business_id',auth()->user()->business_id))],
            'contact_ids.*' => [Rule::exists('contacts','id')->where(fn($q)=>$q->where('business_id',auth()->user()->business_id))],
            'customer_ids.*' => [Rule::exists('contacts','id')->where(fn($q)=>$q->where('business_id',auth()->user()->business_id)->where('type','customer'))],
        ]);

        $campaign = Campaign::create([
            'contact_id' => $request->contact_id,
            'campaign_code' => $request->campaign_code,
            'campaign_name' => $request->campaign_name,
            'currency_id' => $request->currency,
            'campaign_type_id' => $request->campaign_type,
            'expected_revenue' => $request->expected_revenue,
            'actual_revenue' => $request->actual_revenue ?: 0,
            'lead_source' => $request->lead_source,
            'budgeted_cost' => $request->budgeted_cost,
            'actual_cost' => $request->actual_cost,
            'expected_response' => $request->expected_response,
            'proposed_start_date' => $request->proposed_start_date,
            'proposed_end_date' => $request->proposed_end_date,
            'actual_start_date' => $request->actual_start_date,
            'actual_end_date' => $request->actual_end_date,
            'number_sent' => $request->number_sent,
            'description' => $request->description,
            'template' => $request->template,
            'status' => $request->status,
            'owner_id' => $request->owner,
            'business_id' => auth()->user()->business_id,
        ]);
        $this->syncTargets($campaign, $request);


        return redirect()->route('campaigns')->with('success', 'Campaign created successfully!');
    }

    public function show(Request $request)
    {

        $campaignId = $request->query('id');
        $campaign = Campaign::with('targets')->findOrFail($campaignId);

        // Check if current user can access this campaign's owner
        $accessibleUserIds = $this->getAccessibleUserIds(auth()->id());
        
        if (!in_array($campaign->owner_id, $accessibleUserIds)) {
            abort(403, 'You do not have permission to view this campaign.');
        }

        $analytics = $this->analytics($campaign);
        return view('campaigns.campaigns-view', compact('campaign','analytics'));
    }

    public function edit(Request $request)
    {
        $campaignId = $request->query('id');

        $campaign = Campaign::findOrFail($campaignId);

        // Check access permission
        $accessibleUserIds = $this->getAccessibleUserIds(auth()->id());
        
        if (!in_array($campaign->owner_id, $accessibleUserIds)) {
            abort(403, 'You do not have permission to edit this campaign.');
        }

        $languages = Language::all();
        $contacts = Contact::all();
        $leads = Lead::all();
        $users = $this->getAccessibleUsers();

        return view('campaigns.campaigns-edit', compact('campaign', 'languages', 'contacts', 'leads', 'users'));
    }



    public function update(Request $request, $id)
    {
        $campaign = Campaign::findOrFail($id);

        // Check access permission
        $accessibleUserIds = $this->getAccessibleUserIds(auth()->id());
        
        if (!in_array($campaign->owner_id, $accessibleUserIds)) {
            abort(403, 'You do not have permission to update this campaign.');
        }

        // Validate that the new owner is accessible
        $request->validate([
            'owner' => 'required|in:' . implode(',', $accessibleUserIds),
            'actual_revenue' => 'nullable|numeric|min:0', 'lead_source' => 'nullable|string|max:255',
            'lead_ids' => 'nullable|array', 'contact_ids' => 'nullable|array', 'customer_ids' => 'nullable|array',
        ]);

        $campaign->update([
            'contact_id' => $request->contact_id,
            'campaign_code' => $request->campaign_code,
            'campaign_name' => $request->campaign_name,
            'currency_id' => $request->currency,
            'campaign_type_id' => $request->campaign_type,
            'expected_revenue' => $request->expected_revenue,
            'actual_revenue' => $request->actual_revenue ?: 0,
            'lead_source' => $request->lead_source,
            'budgeted_cost' => $request->budgeted_cost,
            'actual_cost' => $request->actual_cost,
            'expected_response' => $request->expected_response,
            'proposed_start_date' => $request->proposed_start_date,
            'proposed_end_date' => $request->proposed_end_date,
            'actual_start_date' => $request->actual_start_date,
            'actual_end_date' => $request->actual_end_date,
            'number_sent' => $request->number_sent,
            'description' => $request->description,
            'template' => $request->template,
            'status' => $request->status,
            'owner_id' => $request->owner,
            'business_id' => auth()->user()->business_id,
        ]);
        $this->syncTargets($campaign, $request);


        return redirect()->route('campaigns')->with('success', 'Campaign updated successfully!');
    }



    public function destroy($id)
    {
        // Find the lead by ID
        $campaign = Campaign::findOrFail($id);

        // Check access permission
        $accessibleUserIds = $this->getAccessibleUserIds(auth()->id());
        
        if (!in_array($campaign->owner_id, $accessibleUserIds)) {
            abort(403, 'You do not have permission to delete this campaign.');
        }

        $campaign->delete();

        return redirect()->route('campaigns')->with('success', 'Campaign and associated Contact deleted successfully!');
    }


    public function bulkmail(Request $request)
    {
        $filters = $request->validate([
            'q' => ['nullable', 'string', 'max:100'],
            'email_status' => ['nullable', Rule::in(['draft', 'scheduled', 'sent', 'opened', 'replied', 'failed'])],
            'campaign_id' => ['nullable', 'integer'],
            'date_from' => ['nullable', 'date'],
            'date_to' => ['nullable', 'date', 'after_or_equal:date_from'],
        ]);
        $businessId = auth()->user()->business_id;
        $emails = Activity::query()
            ->with(['owner'])
            ->where('business_id', $businessId)
            ->where('activity_type_id', 9)
            ->when($request->filled('q'), function ($query) use ($request) {
                $term = '%'.addcslashes($request->string('q')->trim()->value(), '%_').'%' ;
                $query->where(fn ($q) => $q->where('subject', 'like', $term)
                    ->orWhere('email_to', 'like', $term)->orWhere('from_email', 'like', $term));
            })
            ->when($request->filled('email_status'), fn ($q) => $q->where('email_status', $request->email_status))
            ->when($request->filled('campaign_id'), fn ($q) => $q->where('related_campaign_id', $request->integer('campaign_id')))
            ->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))
            ->latest('id')->paginate(25)->withQueryString();
        $campaigns = Campaign::where('business_id', $businessId)->orderBy('campaign_name')->get(['id', 'campaign_name']);
        $campaignNames = $campaigns->pluck('campaign_name', 'id');

        return view('campaigns.campaigns-email-list', compact('emails', 'campaigns', 'campaignNames', 'filters'));
    }

    private function syncTargets(Campaign $campaign, Request $request): void
    {
        $targets = collect($request->input('lead_ids', []))->map(fn($id)=>[Lead::class,(int)$id])
            ->merge(collect($request->input('contact_ids', []))->map(fn($id)=>[Contact::class,(int)$id]))
            ->merge(collect($request->input('customer_ids', []))->map(fn($id)=>[Contact::class,(int)$id]))
            ->unique(fn($item)=>$item[0].':'.$item[1]);
        $keys = $targets->map(fn($item)=>$item[0].':'.$item[1])->all();
        foreach($targets as [$type,$id]) CampaignTarget::withTrashed()->updateOrCreate(
            ['campaign_id'=>$campaign->id,'target_type'=>$type,'target_id'=>$id],
            ['deleted_at'=>null,'business_id'=>$campaign->business_id]
        );
        foreach($campaign->targets()->get() as $target) if(!in_array($target->target_type.':'.$target->target_id,$keys,true)) $target->delete();
    }

    private function analytics(Campaign $campaign): array
    {
        $query=$campaign->targets();
        $total=(clone $query)->count(); $responded=(clone $query)->whereNotNull('response_status')->count();
        $converted=(clone $query)->where('converted',true)->count();
        return ['total'=>$total,'sent'=>(clone $query)->whereNotNull('sent_at')->count(),'responded'=>$responded,
            'converted'=>$converted,'response_rate'=>$total?round($responded/$total*100,1):0,
            'conversion_rate'=>$total?round($converted/$total*100,1):0,
            'conversion_value'=>(float)(clone $query)->sum('conversion_value'),
            'roi'=>$campaign->actual_cost>0?round((($campaign->actual_revenue-$campaign->actual_cost)/$campaign->actual_cost)*100,1):0];
    }
}
