<?php

namespace App\Http\Controllers;

use App\Constants\Status;
use App\Lib\CurlRequest;
use App\Lib\TicketStore;
use App\Models\CronJob;
use App\Models\CronJobLog;
use App\Models\Department;
use App\Models\SpamFilter;
use App\Models\SupportTicket;
use App\Models\SupportTicketReply;
use App\Models\TicketLog;
use App\Models\TicketStatus;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Str;

class CronController extends Controller
{
    public function cron()
    {
        $general            = gs();
        $general->last_cron = now();
        $general->save();

        $crons = CronJob::with('schedule');

        if (request()->alias) {
            $crons->where('alias', request()->alias);
        } else {
            $crons->where('next_run', '<', now())->where('is_running', Status::YES);
        }

        $crons = $crons->get();
        foreach ($crons as $cron) {
            $cronLog              = new CronJobLog();
            $cronLog->cron_job_id = $cron->id;
            $cronLog->start_at    = now();
            if ($cron->is_default) {
                $controller = new $cron->action[0];
                try {
                    $method = $cron->action[1];
                    $controller->$method();
                } catch (\Exception $e) {
                    $cronLog->error = $e->getMessage();
                }
            } else {
                try {
                    CurlRequest::curlContent($cron->url);
                } catch (\Exception $e) {
                    $cronLog->error = $e->getMessage();
                }
            }
            $cron->last_run = now();
            $cron->next_run = now()->addSeconds($cron->schedule->interval);
            $cron->save();

            $cronLog->end_at = $cron->last_run;

            $startTime         = Carbon::parse($cronLog->start_at);
            $endTime           = Carbon::parse($cronLog->end_at);
            $diffInSeconds     = $startTime->diffInSeconds($endTime);
            $cronLog->duration = $diffInSeconds;
            $cronLog->save();
        }
        if (request()->target == 'all') {
            $notify[] = ['success', 'Cron executed successfully'];
            return back()->withNotify($notify);
        }
        if (request()->alias) {
            $notify[] = ['success', keyToTitle(request()->alias) . ' executed successfully'];
            return back()->withNotify($notify);
        }
    }

    public function autoClose()
    {
        $closedStatus = TicketStatus::where('slug', 'closed')->first();

        $tickets = SupportTicket::with('department', 'user')->where('ticket_status_id', '!=', $closedStatus->id)->with(['ticketLogs' => function ($ticketLog) {
            $ticketLog->latest()->take(1);
        }])->get();

        foreach ($tickets as $ticket) {
            $lastActive   = $ticket->ticketLogs->first()->created_at ?? $ticket->created_at;
            $autoCloseDay = gs('auto_close');

            if ($lastActive->addDays($autoCloseDay)->isFuture()) {
                continue;
            }

            $ticket->ticket_status_id = $closedStatus->id;
            $ticket->save();

            TicketLog::storeLog("Status changed to $closedStatus->name by system", $ticket->id, remark: 'closed', ticket: $ticket);
        }
    }

    public function fetchMail()
    {
        $departments = Department::active()->where('pipe_only', Status::YES)->orderBy('last_update')->limit(2)->get();
        $departments = Department::active()->where('pipe_only', Status::YES)->get();

        foreach ($departments as $department) {
            $mailbox  = "{" . $department->host . ":" . $department->port . "/imap/ssl}INBOX";
            $username = $department->email_user;
            $password = $department->email_password;

            $inbox = imap_open($mailbox, $username, $password);

            $sinceDate = date('d-M-Y', strtotime($department->last_inbox_check));
            $emails    = imap_search($inbox, 'SINCE "' . $sinceDate . '"');

            if ($emails) {
                foreach ($emails as $key => $emailNumber) {
                    $overview       = imap_fetch_overview($inbox, $emailNumber, 0);
                    $from           = $overview[0]->from ? $this->getMailAddress($overview[0]->from) : null;
                    $messageId      = $overview[0]->message_id ?? null;
                    $inReplyTo      = $overview[0]->in_reply_to ?? null;
                    $lastInboxCheck = $overview[0]->udate ?? null;
                    if (!$inReplyTo) {
                        $supportTicket = SupportTicket::where('email_message_id', $messageId)->exists();
                        if ($supportTicket) {
                            continue;
                        }
                    }

                    $subject      = '';
                    $subjectParts = imap_mime_header_decode($overview[0]->subject);
                    foreach ($subjectParts as $part) {
                        $subject .= $part->text;
                    }

                    $structure = imap_fetchstructure($inbox, $emailNumber);
                    $message   = $this->extractMessageBody($structure, $inbox, $emailNumber, true);
             

                    if (isset($structure->parts)) {
                        $tempFiles = [];
                        foreach ($structure->parts as $partNum => $part) {
                            if (isset($part->disposition) && strtolower($part->disposition) === 'attachment') {
                                $attachment = imap_fetchbody($inbox, $emailNumber, $partNum + 1);

                                if ($part->encoding == 3) {
                                    $attachment = base64_decode($attachment);
                                } elseif ($part->encoding == 4) {
                                    $attachment = quoted_printable_decode($attachment);
                                }

                                $filename          = $part->dparameters[0]->value ?? 'unnamed';
                                $extension         = pathinfo($filename, PATHINFO_EXTENSION);
                                $allowedExtensions = (new TicketStore(request()))->getAllowedExtension();

                                if (!in_array($extension, $allowedExtensions)) {
                                    continue;
                                }

                                $directory = getFilePath('tempSupportTicket');

                                if (!is_dir($directory)) {
                                    mkdir($directory, 0755, true);
                                }

                                $tempPath = $directory . '/' . Str::random(40) . '.' . $extension;
                                file_put_contents($tempPath, $attachment);

                                $uploadedFile = new UploadedFile($tempPath, $filename, mime_content_type($tempPath), null, true);
                                $tempFiles[]  = $uploadedFile;
                            }

                        }
                    }

                    if (empty($message)) {
                        $message = imap_fetchbody($inbox, $emailNumber, 1);
                    }
                    if ($inReplyTo) {
                        $ticketNumber  = $this->getTicketNumber($subject);
                        $supportTicket = SupportTicket::where('ticket_number', $ticketNumber)->first();
                        if (!$supportTicket) {
                            continue;
                        }

                        $description = $this->extractReplyText($this->htmlToPlainText($message));
                        $isSpam = SpamFilter::isSpam($description) || SpamFilter::isSpam($from, 'email');

                        if ($isSpam) {
                            continue;
                        }

                        $replyInserted = SupportTicketReply::where('support_ticket_id', $supportTicket->id)->where('reply_id', $inReplyTo)->first();

                        if ($replyInserted) {
                            continue;
                        }

                        $request = new Request([
                            'description' => $description,
                            'reply_id'    => $inReplyTo,
                        ]);

                        if (!empty($tempFiles)) {
                            $request->files->add(['attachments' => $tempFiles]);
                        }

                        $ticketStore = new TicketStore($request, Status::CREATED_VIA_EMAIL);
                        $validation  = $ticketStore->replyValidation($supportTicket);
                        if ($validation !== true) {
                            return $validation;
                        }

                        $ticketStore->storeReply();
                    } else {
                        $supportTicket = SupportTicket::where('email_message_id', $messageId)->exists();
                        if ($supportTicket) {
                            continue;
                        }

                        $plainMessage = $this->htmlToPlainText($message);
                        $isSpam       = SpamFilter::isSpam($plainMessage, subject: $subject) || SpamFilter::isSpam($from, 'email');

                        if ($isSpam) {
                            continue;
                        }

                        $openStatus = TicketStatus::where('slug', 'open')->first();

                        $request = new Request([
                            'department'       => $department->id,
                            'user_email'       => $from,
                            'subject'          => $subject,
                            'description'      => $plainMessage,
                            'email_message_id' => $messageId,
                            'status'           => $openStatus->id,
                            'priority'         => gs('default_priority'),
                        ]);

                        if (!empty($tempFiles)) {
                            $request->files->add(['attachments' => $tempFiles]);
                        }

                        $ticketStore = new TicketStore($request, Status::CREATED_VIA_EMAIL);
                        $validation  = $ticketStore->validation();

                        if ($validation !== true) {
                            continue;
                        }

                        $ticketStore->handleUser();
                        $ticket = $ticketStore->storeSupportTicket();

                        $ticketStore->storeReply(true);

                        if ($ticket->department->auto_respond) {
                            $ticket->sendAutoResponse();
                        }
                    }
                }

                if ($lastInboxCheck) {
                    $datetime                     = date('Y-m-d', $lastInboxCheck);
                    $department->last_inbox_check = $datetime;
                    $department->save();
                }
            }

            imap_close($inbox);

            $department->last_update = time();
            $department->save();
        }

        $this->deleteAllTempFile();
    }

    private function extractMessageBody($structure, $inbox, $emailNumber, $preferHtml = false, $partNum = '')
    {
        if (!isset($structure->parts)) {
            return '';
        }

        $messagePlain = '';
        $messageHtml  = '';

        foreach ($structure->parts as $index => $part) {
            $currentPartNum = $partNum === '' ? ($index + 1) : ($partNum . '.' . ($index + 1));

            if ($part->type == 0) {
                $body = imap_fetchbody($inbox, $emailNumber, $currentPartNum);

                if ($part->encoding == 3) {
                    $body = base64_decode($body);
                } elseif ($part->encoding == 4) {
                    $body = quoted_printable_decode($body);
                }

                if (strtolower($part->subtype ?? '') == 'plain') {
                    $messagePlain = $body;
                } elseif (strtolower($part->subtype ?? '') == 'html') {
                    $messageHtml = $body;
                }
            }

            if (isset($part->parts)) {
                $nested = $this->extractMessageBody($part, $inbox, $emailNumber, $preferHtml, $currentPartNum);
                if ($nested) {
                    if ($preferHtml && strpos($nested, '<') !== false) {
                        return $nested;
                    }

                    if (!$preferHtml && !empty($nested)) {
                        return $nested;
                    }

                }
            }
        }

        return $preferHtml ? ($messageHtml ?: $messagePlain): ($messagePlain ?: $messageHtml);
    }

    private function htmlToPlainText($html)
    {
        $html = preg_replace('#<script(.*?)>(.*?)</script>#is', '', $html);
        $html = preg_replace('#<style(.*?)>(.*?)</style>#is', '', $html);

        $html = preg_replace('#<(br|BR)\s*/?>#i', "\n", $html);
        $html = preg_replace('#</p>#i', "\n", $html);

        $text = strip_tags($html);

        $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5);

        $text = str_replace("\xC2\xA0", ' ', $text);

        $text = preg_replace('/[ \t]+/', ' ', $text);
        $text = preg_replace('/\s*\n\s*/', "\n", $text);
        $text = preg_replace('/\n{2,}/', "\n\n", $text);
        $text = trim($text);

        return $text;
    }

    private function extractReplyText($message)
    {
        $pattern = '/ThisIsCommentWriteAbove/u';

        if (preg_match($pattern, $message, $matches, PREG_OFFSET_CAPTURE)) {
            $position = $matches[0][1];
            return trim(substr($message, 0, $position));
        }

        return trim($message);
    }

    private function deleteAllTempFile()
    {
        $folderPath = getFilePath('tempSupportTicket');
        $files      = glob($folderPath . '/*');

        foreach ($files as $file) {
            if (is_file($file)) {
                unlink($file);
            }
        }
    }

    private function getMailAddress($form)
    {
        if (preg_match('/[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}/i', $form, $matches)) {
            return $matches[0];
        }
        return null;
    }

    private function getTicketNumber($subject)
    {
        $pos = strrpos($subject, '#');

        if ($pos === false || $pos === strlen($subject) - 1) {
            return null;
        }

        return substr($subject, $pos + 1);
    }

}
