<?php

namespace Aowow;

if (!defined('AOWOW_REVISION'))
    die('illegal access');


class Report
{
    public const MODE_GENERAL         = 0;
    public const MODE_COMMENT         = 1;
    public const MODE_FORUM_POST      = 2;
    public const MODE_SCREENSHOT      = 3;
    public const MODE_CHARACTER       = 4;
    public const MODE_VIDEO           = 5;
    public const MODE_GUIDE           = 6;

    public const GEN_FEEDBACK         = 1;
    public const GEN_BUG_REPORT       = 2;
    public const GEN_TYPO_TRANSLATION = 3;
    public const GEN_OP_ADVERTISING   = 4;
    public const GEN_OP_PARTNERSHIP   = 5;
    public const GEN_PRESS_INQUIRY    = 6;
    public const GEN_MISCELLANEOUS    = 7;
    public const GEN_MISINFORMATION   = 8;
    public const CO_ADVERTISING       = 15;
    public const CO_INACCURATE        = 16;
    public const CO_OUT_OF_DATE       = 17;
    public const CO_SPAM              = 18;
    public const CO_INAPPROPRIATE     = 19;
    public const CO_MISCELLANEOUS     = 20;
    public const FO_ADVERTISING       = 30;
    public const FO_AVATAR            = 31;
    public const FO_INACCURATE        = 32;
    public const FO_OUT_OF_DATE       = 33;
    public const FO_SPAM              = 34;
    public const FO_STICKY_REQUEST    = 35;
    public const FO_INAPPROPRIATE     = 36;
    public const FO_MISCELLANEOUS     = 37;
    public const SS_INACCURATE        = 45;
    public const SS_OUT_OF_DATE       = 46;
    public const SS_INAPPROPRIATE     = 47;
    public const SS_MISCELLANEOUS     = 48;
    public const PR_INACCURATE_DATA   = 60;
    public const PR_MISCELLANEOUS     = 61;
    public const VI_INACCURATE        = 45;
    public const VI_OUT_OF_DATE       = 46;
    public const VI_INAPPROPRIATE     = 47;
    public const VI_MISCELLANEOUS     = 48;
    public const AR_INACCURATE        = 45;
    public const AR_OUT_OF_DATE       = 46;
    public const AR_MISCELLANEOUS     = 48;

    private array $context = array(
        self::MODE_GENERAL => array(
            self::GEN_FEEDBACK         => true,
            self::GEN_BUG_REPORT       => true,
            self::GEN_TYPO_TRANSLATION => true,
            self::GEN_OP_ADVERTISING   => true,
            self::GEN_OP_PARTNERSHIP   => true,
            self::GEN_PRESS_INQUIRY    => true,
            self::GEN_MISCELLANEOUS    => true,
            self::GEN_MISINFORMATION   => true
        ),
        self::MODE_COMMENT => array(
            self::CO_ADVERTISING   => U_GROUP_MODERATOR,
            self::CO_INACCURATE    => true,
            self::CO_OUT_OF_DATE   => true,
            self::CO_SPAM          => U_GROUP_MODERATOR,
            self::CO_INAPPROPRIATE => U_GROUP_MODERATOR,
            self::CO_MISCELLANEOUS => U_GROUP_MODERATOR
        ),
        self::MODE_FORUM_POST => array(
            self::FO_ADVERTISING    => U_GROUP_MODERATOR,
            self::FO_AVATAR         => true,
            self::FO_INACCURATE     => true,
            self::FO_OUT_OF_DATE    => U_GROUP_MODERATOR,
            self::FO_SPAM           => U_GROUP_MODERATOR,
            self::FO_STICKY_REQUEST => U_GROUP_MODERATOR,
            self::FO_INAPPROPRIATE  => U_GROUP_MODERATOR
        ),
        self::MODE_SCREENSHOT => array(
            self::SS_INACCURATE    => true,
            self::SS_OUT_OF_DATE   => true,
            self::SS_INAPPROPRIATE => U_GROUP_MODERATOR,
            self::SS_MISCELLANEOUS => U_GROUP_MODERATOR
        ),
        self::MODE_CHARACTER => array(
            self::PR_INACCURATE_DATA => true,
            self::PR_MISCELLANEOUS   => true
        ),
        self::MODE_VIDEO => array(
            self::VI_INACCURATE    => true,
            self::VI_OUT_OF_DATE   => true,
            self::VI_INAPPROPRIATE => U_GROUP_MODERATOR,
            self::VI_MISCELLANEOUS => U_GROUP_MODERATOR
        ),
        self::MODE_GUIDE => array(
            self::AR_INACCURATE    => true,
            self::AR_OUT_OF_DATE   => true,
            self::AR_MISCELLANEOUS => true
        )
    );

    private const ERR_NONE             = 0;                 // aka: success
    private const ERR_INVALID_CAPTCHA  = 1;                 // captcha not in use
    private const ERR_DESC_TOO_LONG    = 2;
    private const ERR_NO_DESC          = 3;
    private const ERR_ALREADY_REPORTED = 7;
    private const ERR_MISCELLANEOUS    = -1;

    public  const STATUS_OPEN           = 0;
    public  const STATUS_ASSIGNED       = 1;
    public  const STATUS_CLOSED_WONTFIX = 2;
    public  const STATUS_CLOSED_SOLVED  = 3;

    private int $errorCode = self::ERR_NONE;


    public function __construct(private int $mode, private int $reason, private ?int $subject = 0)
    {
        if ($mode < 0 || $reason <= 0)
        {
            trigger_error('Report - malformed contact request received', E_USER_ERROR);
            $this->errorCode = self::ERR_MISCELLANEOUS;
            return;
        }

        if (!isset($this->context[$mode][$reason]))
        {
            trigger_error('Report - report has invalid context (mode:'.$mode.' / reason:'.$reason.')', E_USER_ERROR);
            $this->errorCode = self::ERR_MISCELLANEOUS;
            return;
        }

        if (!User::isLoggedIn() && !User::$ip)
        {
            trigger_error('Report - could not determine IP for anonymous user', E_USER_ERROR);
            $this->errorCode = self::ERR_MISCELLANEOUS;
            return;
        }

        $this->subject ??= 0;                               // 0 for utility, tools and misc pages?
    }

    private function checkTargetContext() : int
    {
        // check already reported
        $field = User::isLoggedIn() ? 'userId' : 'ip';
        if (DB::Aowow()->selectCell('SELECT 1 FROM ?_reports WHERE `mode` = ?d AND `reason`= ?d AND `subject` = ?d AND ?# = ?', $this->mode, $this->reason, $this->subject, $field, User::$id ?: User::$ip))
            return self::ERR_ALREADY_REPORTED;

        // check targeted post/postOwner staff status
        $ctxCheck = $this->context[$this->mode][$this->reason];
        if (is_int($ctxCheck))
        {
            $roles = User::$groups;
            if ($this->mode == self::MODE_COMMENT)
                $roles = DB::Aowow()->selectCell('SELECT `roles` FROM ?_comments WHERE `id` = ?d', $this->subject);
        //  else if if ($this->mode == self::MODE_FORUM_POST)
        //      $roles = DB::Aowow()->selectCell('SELECT `roles` FROM ?_forum_posts WHERE `id` = ?d', $this->subject);

            return $roles & $ctxCheck ? self::ERR_NONE : self::ERR_MISCELLANEOUS;
        }
        else
            return $ctxCheck ? self::ERR_NONE : self::ERR_MISCELLANEOUS;

        // Forum not in use, else:
        //  check post owner
        //      User::$id == post.op && !post.sticky;
        //  check user custom avatar
        //      g_users[post.user].avatar == 2 && (post.roles & U_GROUP_MODERATOR) == 0
    }

    public function create(string $desc, ?string $userAgent = null, ?string $appName = null, ?string $pageUrl = null, ?string $relUrl = null, ?string $email = null) : bool
    {
        if ($this->errorCode)
            return false;

        if (!$desc)
        {
            $this->errorCode = self::ERR_NO_DESC;
            return false;
        }

        if (mb_strlen($desc) > 500)
        {
            $this->errorCode = self::ERR_DESC_TOO_LONG;
            return false;
        }

        if($err = $this->checkTargetContext())
        {
            $this->errorCode = $err;
            return false;
        }

        $update = array(
            'userId'      => User::$id,
            'createDate'  => time(),
            'mode'        => $this->mode,
            'reason'      => $this->reason,
            'subject'     => $this->subject,
            'ip'          => User::$ip,
            'description' => $desc,
            'userAgent'   => $userAgent ?: User::$agent,
            'appName'     => $appName ?: (get_browser(null, true)['browser'] ?: '')
        );

        if ($pageUrl)
            $update['url'] = $pageUrl;

        if ($relUrl)
            $update['relatedurl'] = $relUrl;

        if ($email)
            $update['email'] = $email;

        return DB::Aowow()->query('INSERT INTO ?_reports (?#) VALUES (?a)', array_keys($update), array_values($update));
    }

    public function getSimilar(int ...$status) : array
    {
        if ($this->errorCode)
            return [];

        foreach ($status as &$s)
            if ($s < self::STATUS_OPEN || $s > self::STATUS_CLOSED_SOLVED)
                unset($s);

        return DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, r.* FROM ?_reports r WHERE {`status` IN (?a) AND }`mode` = ?d AND `reason` = ?d AND `subject` = ?d',
            $status ?: DBSIMPLE_SKIP, $this->mode, $this->reason, $this->subject);
    }

    public function close(int $closeStatus, bool $inclAssigned = false) : bool
    {
        if ($closeStatus != self::STATUS_CLOSED_SOLVED && $closeStatus != self::STATUS_CLOSED_WONTFIX)
            return false;

        if (!User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_MOD))
            return false;

        $fromStatus = [self::STATUS_OPEN];
        if ($inclAssigned)
            $fromStatus[] = self::STATUS_ASSIGNED;

        if ($reports = DB::Aowow()->selectCol('SELECT `id` AS ARRAY_KEY, `userId` FROM ?_reports WHERE `status` IN (?a) AND `mode` = ?d AND `reason` = ?d AND `subject` = ?d',
            $fromStatus, $this->mode, $this->reason, $this->subject))
        {
            DB::Aowow()->query('UPDATE ?_reports SET `status` = ?d, `assigned` = 0 WHERE `id` IN (?a)', $closeStatus, array_keys($reports));

            foreach ($reports as $rId => $uId)
                Util::gainSiteReputation($uId, $closeStatus == self::STATUS_CLOSED_SOLVED ? SITEREP_ACTION_GOOD_REPORT : SITEREP_ACTION_BAD_REPORT, ['id' => $rId]);

            return true;
        }

        return false;
    }

    public function reopen(int $assignedTo = 0) : bool
    {
        // assignedTo = 0 ? status = STATUS_OPEN : status = STATUS_ASSIGNED, userId = assignedTo
        return false;
    }

    public function getError() : int
    {
        return $this->errorCode;
    }
}

?>
