<?php

namespace App\Controllers\Api;

use App\Models\MemberModel;
use App\Models\MemberInvitationModel;

class MembersController extends ApiController
{
    protected $memberModel;
    protected $memberInvitationModel;

    public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
    {
        parent::initController($request, $response, $logger);
        
        try {
            $this->memberModel = new MemberModel();
            $this->memberInvitationModel = new MemberInvitationModel();
        } catch (\Exception $e) {
            log_message('error', 'MembersController init error: ' . $e->getMessage());
        }
    }

    public function index()
    {
        if (!$this->hasAnyRole(['admin', 'finance'])) {
            return $this->respond(['error' => 'Forbidden'], 403);
        }

        $search = $this->request->getGet('search');
        $status = $this->request->getGet('status');
        $page = max(1, (int) ($this->request->getGet('page') ?: 1));
        $limit = max(1, (int) ($this->request->getGet('limit') ?: 20));
        $builder = $this->memberModel;

        if ($search) {
            $builder = $builder
                ->groupStart()
                ->like('full_name', $search)
                ->orLike('roll_no', $search)
                ->orLike('email', $search)
                ->groupEnd();
        }

        if ($status) {
            $builder = $builder->where('status', $status);
        }

        $total = $builder->countAllResults(false);
        $members = $builder
            ->orderBy('id', 'DESC')
            ->findAll($limit, ($page - 1) * $limit);
        $totalPages = (int) max(1, ceil($total / $limit));

        return $this->respond([
            'members' => $members,
            'page' => $page,
            'limit' => $limit,
            'total' => $total,
            'total_pages' => $totalPages,
            'has_next' => $page < $totalPages,
            'has_previous' => $page > 1,
        ]);
    }

    public function create()
    {
        if (!$this->hasAnyRole(['admin'])) {
            return $this->respond(['error' => 'Forbidden'], 403);
        }

        if (!$this->request->is('post')) {
            return $this->respondBadRequest('Invalid request method');
        }

        $data = $this->request->getJSON();

        $memberData = [
            'user_id' => $data->user_id ?? null,
            'roll_no' => $data->roll_no,
            'full_name' => $data->full_name,
            'phone' => $data->phone,
            'email' => $data->email,
            'date_of_birth' => $data->date_of_birth,
            'gender' => $data->gender ?? null,
            'status' => 'active',
            'admission_date' => $data->admission_date ?? date('Y-m-d'),
            'category' => $data->category ?? 0,
        ];

        if ($this->memberModel->insert($memberData)) {
            $memberId = $this->memberModel->insertID();
            $this->logAudit('create', 'member', $memberId, null, $memberData);
            return $this->respondCreated(['memberId' => $memberId, 'message' => 'Member created successfully']);
        }

        return $this->respondBadRequest('Failed to create member: ' . implode(', ', $this->memberModel->errors()));
    }

    public function show($id)
    {
        if (!$this->isAuthenticated()) {
            return $this->respond(['error' => 'Unauthorized'], 401);
        }

        $member = $this->memberModel->find($id);

        if (!$member) {
            return $this->respondNotFound();
        }

        return $this->respond($member);
    }

    public function me()
    {
        if (!$this->isAuthenticated()) {
            return $this->respond(['error' => 'Unauthorized'], 401);
        }

        $member = $this->memberModel->getMemberByUserId($this->currentUser['userId']);

        if (!$member) {
            return $this->respond([
                'error' => 'Member profile not found'
            ], 404);
        }

        return $this->respond($member);
    }

    private function hasInvitationPurposeSupport(): bool
    {
        return $this->memberInvitationModel->hasPurposeColumn();
    }

    public function invite()
    {
        if (!$this->hasRole('admin')) {
            return $this->respond(['error' => 'Forbidden'], 403);
        }

        if (!$this->request->is('post')) {
            return $this->respondBadRequest('Invalid request method');
        }

        $data = $this->request->getJSON();

        if (!isset($data->roll_no) || !isset($data->email) || !isset($data->phone)) {
            return $this->respondValidationError([
                'roll_no' => 'Roll number is required',
                'email' => 'Email is required',
                'phone' => 'Phone is required',
            ]);
        }

        $member = $this->memberModel->getMemberByRollNo($data->roll_no);
        if (!$member) {
            return $this->respond(['error' => 'Member not found for this roll number'], 404);
        }

        if (!empty($member['user_id'])) {
            $linkedUser = $this->db->table('users')->where('id', $member['user_id'])->get()->getRowArray();

            if ($linkedUser) {
                return $this->respondBadRequest('This member already has a user account.');
            }

            // Clean up broken legacy linkage so the member can be invited again.
            $this->memberModel->skipValidation(true)->update($member['id'], [
                'user_id' => null,
            ]);
            $member['user_id'] = null;
        }

        $emailInUse = $this->db->table('users')->where('email', $data->email)->countAllResults();
        if ($emailInUse > 0) {
            return $this->respondBadRequest('That email is already used by another account.');
        }

        $this->memberModel->update($member['id'], [
            'email' => $data->email,
            'phone' => $data->phone,
        ]);

        $token = bin2hex(random_bytes(32));
        $expiresAt = date('Y-m-d H:i:s', strtotime('+7 days'));

        $previousInvitationQuery = $this->memberInvitationModel
            ->where('member_id', $member['id'])
            ->where('accepted_at', null);

        if ($this->hasInvitationPurposeSupport()) {
            $previousInvitationQuery = $previousInvitationQuery->where('purpose', 'register');
        }

        $previousInvitationQuery->set(['accepted_at' => date('Y-m-d H:i:s')])->update();

        $invitationData = [
            'member_id' => $member['id'],
            'roll_no' => (string) $data->roll_no,
            'email' => $data->email,
            'phone' => $data->phone,
            'token' => $token,
            'expires_at' => $expiresAt,
            'created_by' => $this->currentUser['userId'] ?? null,
        ];

        if ($this->hasInvitationPurposeSupport()) {
            $invitationData['purpose'] = 'register';
        }

        $this->memberInvitationModel->insert($invitationData);

        $frontendBaseUrl = $this->request->getHeaderLine('Origin') ?: 'http://localhost:5173';
        $invitationUrl = rtrim($frontendBaseUrl, '/') . '/register?token=' . $token;
        $emailSent = $this->sendInvitationEmail($data->email, $member['full_name'], $invitationUrl);

        $this->logAudit('invite', 'member', $member['id'], null, [
            'email' => $data->email,
            'phone' => $data->phone,
            'roll_no' => $data->roll_no,
        ]);

        return $this->respondCreated([
            'message' => $emailSent
                ? 'Invitation sent successfully.'
                : 'Invitation created. Email was not sent automatically; share the link manually.',
            'invitation_url' => $invitationUrl,
            'email_sent' => $emailSent,
            'member' => [
                'id' => $member['id'],
                'full_name' => $member['full_name'],
                'roll_no' => $member['roll_no'],
                'email' => $data->email,
                'phone' => $data->phone,
            ],
        ]);
    }

    public function generatePublicLink($id)
    {
        if (!$this->hasRole('admin')) {
            return $this->respond(['error' => 'Forbidden'], 403);
        }

        if (!$this->request->is('post')) {
            return $this->respondBadRequest('Invalid request method');
        }

        if (!$this->hasInvitationPurposeSupport()) {
            return $this->respond(['error' => 'Database schema missing invitation purpose column; run the latest migrations'], 500);
        }

        $member = $this->memberModel->find($id);
        if (!$member) {
            return $this->respondNotFound();
        }

        $token = bin2hex(random_bytes(32));
        $expiresAt = date('Y-m-d H:i:s', strtotime('+30 days'));

        $this->memberInvitationModel
            ->where('member_id', $member['id'])
            ->where('purpose', 'public_update')
            ->where('accepted_at', null)
            ->set(['accepted_at' => date('Y-m-d H:i:s')])
            ->update();

        $this->memberInvitationModel->insert([
            'member_id' => $member['id'],
            'roll_no' => $member['roll_no'],
            'email' => $member['email'] ?? null,
            'phone' => $member['phone'] ?? null,
            'token' => $token,
            'purpose' => 'public_update',
            'expires_at' => $expiresAt,
            'created_by' => $this->currentUser['userId'] ?? null,
        ]);

        $frontendBaseUrl = $this->request->getHeaderLine('Origin') ?: 'http://localhost:5173';
        $updateUrl = rtrim($frontendBaseUrl, '/') . '/public/update/' . $token;

        return $this->respondCreated([
            'message' => 'Public update link created successfully.',
            'invitation_url' => $updateUrl,
            'expires_at' => $expiresAt,
        ]);
    }

    public function update($id)
    {
        if (!$this->hasAnyRole(['admin'])) {
            return $this->respond(['error' => 'Forbidden'], 403);
        }

        if (!$this->request->is('put')) {
            return $this->respondBadRequest('Invalid request method');
        }

        $member = $this->memberModel->find($id);
        if (!$member) {
            return $this->respondNotFound();
        }

        $data = $this->request->getJSON();

        $updateData = [];
        if (isset($data->full_name)) $updateData['full_name'] = $data->full_name;
        if (isset($data->roll_no)) $updateData['roll_no'] = $data->roll_no;
        if (isset($data->phone)) $updateData['phone'] = $data->phone;
        if (isset($data->email)) $updateData['email'] = $data->email;
        if (isset($data->gender)) $updateData['gender'] = $data->gender;
        if (isset($data->date_of_birth)) $updateData['date_of_birth'] = $data->date_of_birth;
        if (isset($data->date_of_death)) $updateData['date_of_death'] = $data->date_of_death;
        if (isset($data->chapter)) $updateData['chapter'] = $data->chapter;
        if (isset($data->admission_date)) $updateData['admission_date'] = $data->admission_date;
        if (isset($data->specialization)) $updateData['specialization'] = $data->specialization;
        if (isset($data->position)) $updateData['position'] = $data->position;
        if (isset($data->status)) $updateData['status'] = $data->status;
        if (isset($data->firm_address)) $updateData['firm_address'] = $data->firm_address;
        if (isset($data->firm_name)) $updateData['firm_name'] = $data->firm_name;

        if (isset($updateData['roll_no'])) {
            $existingRollNo = $this->memberModel
                ->where('roll_no', $updateData['roll_no'])
                ->where('id !=', $id)
                ->first();

            if ($existingRollNo) {
                return $this->respondBadRequest('Roll number already belongs to another member.');
            }
        }

        if ($this->memberModel->skipValidation(true)->update($id, $updateData)) {
            $this->logAudit('update', 'member', $id, $member, $updateData);
            return $this->respondUpdated(['message' => 'Member updated successfully']);
        }

        return $this->respondBadRequest('Failed to update member: ' . implode(', ', $this->memberModel->errors()));
    }

    public function delete($id)
    {
        if (!$this->hasRole('admin')) {
            return $this->respond(['error' => 'Forbidden'], 403);
        }

        $member = $this->memberModel->find($id);
        if (!$member) {
            return $this->respondNotFound();
        }

        if ($this->memberModel->delete($id)) {
            $this->logAudit('delete', 'member', $id, $member, null);
            return $this->respondDeleted();
        }

        return $this->respondBadRequest('Failed to delete member');
    }

    public function search()
    {
        if (!$this->isAuthenticated()) {
            return $this->respond(['error' => 'Unauthorized'], 401);
        }

        $search = $this->request->getGet('q');

        if (!$search) {
            return $this->respondBadRequest('Search query required');
        }

        $members = $this->memberModel->searchMembers($search);

        return $this->respond(['members' => $members]);
    }

    public function getStatuses()
    {
        if (!$this->hasAnyRole(['admin'])) {
            return $this->respond(['error' => 'Forbidden'], 403);
        }

        $statuses = $this->memberModel
            ->select('status')
            ->distinct()
            ->where('status IS NOT NULL', null, false)
            ->orderBy('status', 'ASC')
            ->findColumn('status');

        return $this->respond(['statuses' => $statuses]);
    }

    public function getStats()
    {
        if (!$this->hasAnyRole(['admin', 'finance'])) {
            return $this->respond(['error' => 'Forbidden'], 403);
        }

        $type = $this->request->getGet('type');

        if ($type === 'deceased') {
            $currentYear = date('Y');
            $totalDeceased = $this->memberModel->getDeceasedMembersCount();

            $fields = $this->db->getFieldNames('member');
            $hasDateOfDeath = in_array('date_of_death', $fields, true);
            $hasDateOfBirth = in_array('date_of_birth', $fields, true);

            $deceasedThisYear = 0;
            if ($hasDateOfDeath) {
                $deceasedThisYear = $this->memberModel->builder()
                    ->where('status', 'deceased')
                    ->where("YEAR(date_of_death) = {$currentYear}", null, false)
                    ->countAllResults();
            }

            $ageGroups = [
                '18-30' => 0,
                '31-50' => 0,
                '51-100' => 0,
            ];

            if ($hasDateOfDeath && $hasDateOfBirth) {
                $ageGroups['18-30'] = (int) $this->memberModel->builder()
                    ->where('status', 'deceased')
                    ->where('date_of_birth IS NOT NULL', null, false)
                    ->where('date_of_death IS NOT NULL', null, false)
                    ->where("TIMESTAMPDIFF(YEAR, date_of_birth, date_of_death) BETWEEN 18 AND 30", null, false)
                    ->countAllResults();

                $ageGroups['31-50'] = (int) $this->memberModel->builder()
                    ->where('status', 'deceased')
                    ->where('date_of_birth IS NOT NULL', null, false)
                    ->where('date_of_death IS NOT NULL', null, false)
                    ->where("TIMESTAMPDIFF(YEAR, date_of_birth, date_of_death) BETWEEN 31 AND 50", null, false)
                    ->countAllResults();

                $ageGroups['51-100'] = (int) $this->memberModel->builder()
                    ->where('status', 'deceased')
                    ->where('date_of_birth IS NOT NULL', null, false)
                    ->where('date_of_death IS NOT NULL', null, false)
                    ->where("TIMESTAMPDIFF(YEAR, date_of_birth, date_of_death) BETWEEN 51 AND 100", null, false)
                    ->countAllResults();
            }

            return $this->respond([
                'total_deceased_members' => $totalDeceased,
                'deceased_this_year' => $deceasedThisYear,
                'age_groups' => $ageGroups,
                'has_date_of_death' => $hasDateOfDeath,
                'has_date_of_birth' => $hasDateOfBirth,
            ]);
        }

        $stats = [
            'active_members' => $this->memberModel->getActiveMembersCount(),
            'total_members' => $this->memberModel->countAllResults(),
        ];

        return $this->respond($stats);
    }

    private function sendInvitationEmail(string $email, string $memberName, string $invitationUrl): bool
    {
        try {
            $emailService = service('email');
            $config = config('Email');

            if (empty($config->fromEmail)) {
                return false;
            }

            $emailService->setTo($email);
            $emailService->setFrom($config->fromEmail, $config->fromName ?: 'AWTF');
            $emailService->setSubject('AWTF Member Invitation');
            $emailService->setMessage(
                "Hello {$memberName},\n\n" .
                "You have been invited to join the AWTF member portal.\n" .
                "Use the link below to set your password and activate your account:\n\n" .
                "{$invitationUrl}\n\n" .
                "This invitation link expires in 7 days."
            );

            return (bool) $emailService->send();
        } catch (\Throwable $e) {
            log_message('error', 'Failed to send invitation email: ' . $e->getMessage());
            return false;
        }
    }
}
