<?php

namespace Battles;

use Battles\Database\Db;

class User
{
    private static ?self $_instance = null;

    protected int $id = 0;
    protected string $login = '';
    protected ?string $pass = null;
    protected ?string $email = null;
    protected ?string $realname = null;
    protected ?string $borndate = null;
    protected ?string $info = null;
    protected int $level = 0;
    protected ?int $align = null;
    protected ?string $clan = null;
    protected ?int $money = null;
    protected ?string $ip = null;

    protected ?int $admin = null;
    protected int $room = 0;
    protected int $block = 0;
    protected string $shadow = '';

    // Пока несуществующие, для совместимости.
    protected int $experience = 0;
    protected int $battle = 0;
    protected int $in_tower = 0; // Скорее башню похороним чем запустим...
    protected int $zayavka = 0;

    public const INFO_CHAR_LIMIT = 1500;

    protected function __construct($user = null)
    {
        if (is_null($user)) {
            $user = $_SESSION['uid'];
        }
        $query = 'select * from users where login = ?';
        if (is_numeric($user)) {
            $query = 'select * from users where id = ?';
            $user = (int)$user;
        }
        $user_query = Db::getInstance()->fetch($query, $user);
        foreach ($this as $key => $value) {
            if (isset($user_query[$key])) {
                $this->$key = $user_query[$key];
            }
        }
    }

    public static function getInstance($user = null): self
    {
        if (is_null(self::$_instance)) {
            self::$_instance = new self($user);
        }
        return self::$_instance;
    }

    /**
     * @param int         $userId
     * @param int         $type
     * @param string      $name
     * @param int         $time
     * @param string|null $json_modifiers_list (str, dex, int, end, intel, wis).
     */
    public static function addUserEffect(int $userId, int $type, string $name, int $time, string $json_modifiers_list = null)
    {
        $mods = json_decode($json_modifiers_list);
        Db::getInstance()->execute('INSERT INTO users_effects (owner_id, type, name, remaining_time, mod_strength, mod_dexterity, mod_intuition, mod_endurance, mod_intelligence, mod_wisdom) VALUES (?,?,?,?,?,?,?,?,?,?)', [$userId, $type, $name, $time, $mods->str ?? null, $mods->dex ?? null, $mods->int ?? null, $mods->end ?? null, $mods->intel ?? null, $mods->wis ?? null]);
    }

    public static function removeUserEffect(int $userId, int $type): bool
    {
        if (Db::getInstance()->fetchColumn('SELECT 1 FROM users_effects WHERE owner_id = ? AND type = ?', [$userId, $type])) {
            Db::getInstance()->execute('DELETE FROM users_effects WHERE owner_id = ? AND type = ?', [$userId, $type]);
        }
        return false;
    }

    public function getId(): int
    {
        return $this->id;
    }

    public function getLogin(): string
    {
        return $this->login;
    }

    public function getPass(): string
    {
        return $this->pass;
    }

    /**
     * @param mixed $pass
     */
    public function setPass($pass): void
    {
        $this->pass = $pass;
    }

    public function getRealname(): string
    {
        return $this->realname;
    }

    /**
     * @param mixed $realname
     */
    public function setRealname($realname): void
    {
        $this->realname = $realname;
    }

    public function getInfo(): string
    {
        return $this->info;
    }

    /**
     * @param mixed $info
     */
    public function setInfo($info)
    {
        $this->info = $info;
    }

    public function getLevel(): int
    {
        return $this->level;
    }

    public function getAlign(): int
    {
        return $this->align;
    }

    public function getClan(): ?string
    {
        return $this->clan;
    }

    /**
     * @param string|null $short_name Короткое название клана. Передать null для очистки.
     */
    public function setClan(?string $short_name)
    {
        $this->clan = is_null($short_name) ? null : $short_name;
        $this->saveUser();
    }

    public function getMoney(): int
    {
        return $this->money;
    }

    public function setMoney(int $money)
    {
        $this->money = max($money, 0);
    }

    public function saveMoney()
    {
        Db::getInstance()->execute('update users set money = ? where id = ?', [$this->money, $this->id]);
    }

    public function getAdmin(): int
    {
        return $this->admin;
    }

    public function getRoom(): int
    {
        return $this->room;
    }

    /**
     * @param mixed $room
     */
    public function setRoom($room)
    {
        $this->room = $room;
    }

    public function getBlock(): int
    {
        return $this->block;
    }

    public function getShadow(): string
    {
        return $this->shadow;
    }

    /**
     * @param mixed $shadow
     */
    public function setShadow($shadow): void
    {
        $shadows = [
            'm01', 'm02', 'm03', 'm04', 'm05', 'm06', 'm07', 'm08', 'm09', 'm10',
            'f01', 'f02', 'f03', 'f04', 'f05', 'f06', 'f07', 'f08', 'f09', 'f10',
        ];
        if (in_array($shadow, $shadows) && $this->getShadow() == '0.png') {
            $this->shadow = $shadow . '.png';
        }
    }

    public function getExperience(): int
    {
        return $this->experience;
    }

    public function addExperience(int $amount): void
    {
        $this->experience += max($amount, 0);
    }

    public function getBattle(): int
    {
        return $this->battle;
    }

    public function getInTower(): int
    {
        return $this->in_tower;
    }

    public function getZayavka(): int
    {
        return $this->zayavka;
    }

    public function setOnline()
    {
        Db::getInstance()->execute('update online set real_time = ? where user_id = ?', [time(), $this->getId()]);
    }

    public function setInjury(int $type): bool
    {
        if (!in_array($type, [11, 12, 13, 14])) {
            return false;
        }
        $names1 = ['разбитый нос', 'сотрясение первой степени', 'потрепанные уши', 'прикушенный язык', 'перелом переносицы', 'растяжение ноги', 'растяжение руки', 'подбитый глаз', 'синяк под глазом', 'кровоточащее рассечение', 'отбитая «пятая точка»', 'заклинившая челюсть', 'выбитый зуб «мудрости»', 'косоглазие'];
        $names2 = ['отбитые почки', 'вывих «вырезано цензурой»', 'сотрясение второй степени', 'оторванное ухо', 'вывих руки', 'оторванные уши', 'поврежденный позвоночник', 'поврежденный копчик', 'разрыв сухожилия', 'перелом ребра', 'перелом двух ребер', 'вывих ноги', 'сломанная челюсть'];
        $names3 = ['пробитый череп', 'разрыв селезенки', 'смещение позвонков', 'открытый перелом руки', 'открытый перелом «вырезано цензурой»', 'излом носоглотки', 'непонятные, но множественные травмы', 'сильное внутреннее кровотечение', 'раздробленная коленная чашечка', 'перелом шеи', 'смещение позвонков', 'открытый перелом ключицы', 'перелом позвоночника', 'вывих позвоночника', 'сотрясение третьей степени'];
        $param_names = ['str', 'dex', 'int', 'end', 'intel', 'wis',];
        shuffle($param_names);
        switch ($type) {
            case 11:
                shuffle($names1);
                $name = UserEffects::$effectName[$type] . ': ' . $names1(0);
                self::addUserEffect($this->id, $type, $name, strtotime('30min'), json_encode([$param_names(0) => -1]));
                break;
            case 12:
                shuffle($names2);
                $name = UserEffects::$effectName[$type] . ': ' . $names2(0);
                self::addUserEffect($this->id, $type, $name, strtotime('3hours'), json_encode([$param_names(0) => mt_rand(-3, -1), $param_names(1) => mt_rand(-3, -1)]));
                break;
            case 13:
                shuffle($names3);
                $name = UserEffects::$effectName[$type] . ': ' . $names3(0);
                self::addUserEffect($this->id, $type, $name, strtotime('12hours'), json_encode([$param_names(0) => mt_rand(-5, -1), $param_names(1) => mt_rand(-5, -1), $param_names(2) => mt_rand(-5, -1)]));
                break;
            default: //type 14
                self::addUserEffect($this->id, $type, UserEffects::$effectName[$type], strtotime('1day'), json_encode([$param_names(0) => -10]));
                break;
        }

        return true;
    }

    /** Сохраняет в базу актуальные логин, пароль, email, имя, дату рождения, текст инфы, склонность, клан, образ, права админа.
     *
     */
    public function saveUser()
    {
        $query = 'update users set login = ?, pass = ?, email = ?, realname = ?, borndate = ?, info = ?, align = ?, clan = ?, shadow = ?, admin = ? where id = ?';
        $vals = [
            $this->login, //set
            $this->pass,
            $this->email,
            $this->realname,
            $this->borndate,
            $this->info,
            $this->align,
            $this->clan,
            $this->shadow,
            $this->admin,
            $this->id //where
        ];
        Db::getInstance()->execute($query, $vals);
    }
}