<?php

namespace App\Services;

use App\Models\Member;
use App\Models\MembershipApplication;
use App\Models\MembershipCategory;
use App\Models\User;
use App\Models\MemberActivityLog;
use App\Services\MembershipCertificateService;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Spatie\Permission\Models\Role;

class MembershipService
{
    protected NotificationService $notificationService;
    protected MembershipCertificateService $certificateService;

    public function __construct(
        NotificationService $notificationService,
        MembershipCertificateService $certificateService
    ) {
        $this->notificationService = $notificationService;
        $this->certificateService  = $certificateService;
    }

    /**
     * Create a member from an approved application.
     */
    public function createFromApplication(MembershipApplication $application, int $approvedBy): Member
    {
        $member = DB::transaction(function () use ($application, $approvedBy) {
            // Create or get user
            $user = User::where('email', $application->email)->first();
            
            if (!$user) {
                $user = User::create([
                    'name'               => $application->full_name,
                    'email'              => $application->email,
                    'username'           => User::generateUsername($application->email),
                    'password'           => Hash::make(Str::random(16)),
                    'email_verified_at'  => now(),
                ]);
            }

            // Assign member role
            $memberRole = Role::where('name', 'Members')->first();
            if ($memberRole && !$user->hasRole('Members')) {
                $user->assignRole('Members');
            }

            // Create member
            $member = Member::create([
                'membership_code' => Member::generateMembershipCode(),
                'user_id' => $user->id,
                'status' => 'active',
                'approval_date' => now(),
                'approved_by' => $approvedBy,
                'created_by' => $approvedBy,
                'membership_start_date' => now(),
                'renewal_type' => 'manual',
                'auto_renewal_enabled' => false,
            ]);

            // Assign membership category
            $member->membershipCategories()->attach($application->membership_category_id, [
                'assigned_date' => now(),
                'assigned_by' => $approvedBy,
                'is_primary' => true,
            ]);

            // Update application status
            $application->update([
                'status' => 'approved',
                'reviewed_by' => $approvedBy,
                'reviewed_at' => now(),
            ]);

            // Log activity
            MemberActivityLog::log(
                'membership_approved',
                "Membership approved for {$application->full_name}",
                $member,
                User::find($approvedBy),
                ['application_id' => $application->id]
            );

            // Send welcome notification
            $this->notificationService->sendWelcome($member);

            return $member;
        });

        // Generate certificate outside the transaction so failure never rolls back member creation
        try {
            $this->certificateService->generateForMember($member);
        } catch (\Throwable $e) {
            \Illuminate\Support\Facades\Log::error('MembershipService: certificate generation failed after application approval', [
                'member_id' => $member->id,
                'error'     => $e->getMessage(),
            ]);
        }

        return $member;
    }

    /**
     * Create a member directly (bypassing application).
     */
    public function createDirect(array $data, int $createdBy): Member
    {
        $member = DB::transaction(function () use ($data, $createdBy) {
            // Create or get user
            $user = User::where('email', $data['email'])->first();
            
            if (!$user) {
                $user = User::create([
                    'name'              => trim(($data['first_name'] ?? '') . ' ' . ($data['last_name'] ?? '')),
                    'email'             => $data['email'],
                    'username'          => User::generateUsername($data['email']),
                    'password'          => Hash::make($data['password'] ?? Str::random(16)),
                    'phone'             => $data['phone'] ?? null,
                    'email_verified_at' => now(),
                ]);
            }

            // Assign member role
            $memberRole = Role::where('name', 'Members')->first();
            if ($memberRole && !$user->hasRole('Members')) {
                $user->assignRole('Members');
            }

            // Create member
            $member = Member::create([
                'membership_code' => Member::generateMembershipCode(),
                'user_id' => $user->id,
                'status' => $data['status'] ?? 'active',
                'approval_date' => now(),
                'approved_by' => $createdBy,
                'created_by' => $createdBy,
                'membership_start_date' => now(),
                'renewal_type' => $data['renewal_type'] ?? 'manual',
                'auto_renewal_enabled' => $data['auto_renewal_enabled'] ?? false,
                'notes' => $data['notes'] ?? null,
            ]);

            // Assign membership categories
            if (isset($data['category_ids']) && is_array($data['category_ids'])) {
                foreach ($data['category_ids'] as $index => $categoryId) {
                    $member->membershipCategories()->attach($categoryId, [
                        'assigned_date' => now(),
                        'assigned_by' => $createdBy,
                        'is_primary' => $index === 0,
                    ]);
                }
            }

            // Log activity
            MemberActivityLog::log(
                'member_created',
                "Member created directly: {$member->full_name}",
                $member,
                User::find($createdBy)
            );

            // Send welcome notification
            $this->notificationService->sendWelcome($member);

            return $member;
        });

        // Generate certificate for active members outside the transaction
        if ($member->status === 'active') {
            try {
                $this->certificateService->generateForMember($member);
            } catch (\Throwable $e) {
                \Illuminate\Support\Facades\Log::error('MembershipService: certificate generation failed after direct member creation', [
                    'member_id' => $member->id,
                    'error'     => $e->getMessage(),
                ]);
            }
        }

        return $member;
    }

    /**
     * Generate a unique membership code.
     */
    public function generateMembershipCode(): string
    {
        return Member::generateMembershipCode();
    }

    /**
     * Assign role to member's user.
     */
    public function assignRole(Member $member, string $roleName): void
    {
        $role = Role::where('name', $roleName)->first();
        if ($role && $member->user) {
            $member->user->assignRole($role);
        }
    }

    /**
     * Remove role from member's user.
     */
    public function removeRole(Member $member, string $roleName): void
    {
        if ($member->user) {
            $member->user->removeRole($roleName);
        }
    }

    /**
     * Suspend a member.
     */
    public function suspend(Member $member, string $reason, int $suspendedBy): void
    {
        $member->update([
            'status' => 'suspended',
            'notes' => $reason,
        ]);

        MemberActivityLog::log(
            'member_suspended',
            "Member suspended: {$reason}",
            $member,
            User::find($suspendedBy)
        );
    }

    /**
     * Reactivate a suspended member.
     */
    public function reactivate(Member $member, int $reactivatedBy): void
    {
        $member->update([
            'status' => 'active',
        ]);

        MemberActivityLog::log(
            'member_reactivated',
            "Member reactivated",
            $member,
            User::find($reactivatedBy)
        );
    }

    /**
     * Assign membership category to member.
     */
    public function assignCategory(Member $member, int $categoryId, int $assignedBy, bool $isPrimary = false): void
    {
        // If setting as primary, remove primary from other categories
        if ($isPrimary) {
            $member->membershipCategories()->updateExistingPivot(
                $member->membershipCategories()->pluck('membership_categories.id')->toArray(),
                ['is_primary' => false]
            );
        }

        $member->membershipCategories()->attach($categoryId, [
            'assigned_date' => now(),
            'assigned_by' => $assignedBy,
            'is_primary' => $isPrimary,
        ]);

        MemberActivityLog::log(
            'category_assigned',
            "Membership category assigned",
            $member,
            User::find($assignedBy),
            ['category_id' => $categoryId]
        );
    }

    /**
     * Remove membership category from member.
     */
    public function removeCategory(Member $member, int $categoryId, int $removedBy): void
    {
        $member->membershipCategories()->detach($categoryId);

        MemberActivityLog::log(
            'category_removed',
            "Membership category removed",
            $member,
            User::find($removedBy),
            ['category_id' => $categoryId]
        );
    }

    /**
     * Get membership statistics.
     */
    public function getStatistics(): array
    {
        return [
            'total_members' => Member::count(),
            'active_members' => Member::active()->count(),
            'pending_members' => Member::pending()->count(),
            'suspended_members' => Member::suspended()->count(),
            'new_this_month' => Member::whereMonth('created_at', now()->month)
                ->whereYear('created_at', now()->year)
                ->count(),
            'new_this_year' => Member::whereYear('created_at', now()->year)->count(),
            'by_category' => MembershipCategory::withCount('members')->get()->map(function ($category) {
                return [
                    'category' => $category->name,
                    'count' => $category->members_count,
                ];
            }),
        ];
    }
}
