import random

from flask import Flask
from flask_cors import CORS
from flask_restx import Api, Resource

from .functions.build_character_sheet import build_character_sheet
from .functions.roll_ability_scores import roll_ability_scores
from .functions.roll_ability_check import roll_ability_check
from .functions.roll_dices import roll_dices
from .functions.roll_encounter import roll_encounter
from .functions.roll_mental_attack import roll_mental_attack
from .functions.roll_mutations import roll_mutations
from .models.models import dice_model, ability_model, hp_model, character_model, encounter_model, ma_model, \
    mutation_model, \
    check_model
from .schemas.schemas import DiceSchema, CharacterSchema, EncounterSchema, MentalAttackSchema, AbilitySchema, \
    HPSchema, MutationSchema, CheckSchema

app = Flask(__name__)
CORS(app)

app.config.SWAGGER_UI_JSONEDITOR = True
app.config['SWAGGER_UI_JSONEDITOR'] = True
api = Api(app, version='1.0', title='Gamma World Dice', description='Rolled Dice As A Service')

dice = api.namespace('dice', description='Dice operations')
ability = api.namespace('ability', description='Ability operations')
hp = api.namespace('hp', description='HP operations')
ma = api.namespace('ma', description='Mental Attack operations')
mut = api.namespace('mut', description='Mutation operations')
character = api.namespace('character', description='Character operations')
encounter = api.namespace('encounter', description='Encounter operations')
check = api.namespace('check', description='Check operations')

check_model = check.model('Check', check_model)
check_schema = CheckSchema()

ability_model = ability.model('Ability', ability_model)
ability_schema = AbilitySchema()

mutation_model = mut.model('Mutation', mutation_model)
mutation_schema = MutationSchema()

hp_model = hp.model('HP', hp_model)
hp_schema = HPSchema()

dice_model = dice.model('Dice', dice_model)
dice_schema = DiceSchema()

ma_model = ma.model('MA', ma_model)
ma_schema = MentalAttackSchema()

character_model = character.model('Character', character_model)
character_schema = CharacterSchema()

encounter_model = encounter.model('Encounter', encounter_model)
encounter_schema = EncounterSchema()


@api.route('/roll/dice', methods=['POST'])
class RollDice(Resource):
    @dice.expect(dice_model)
    def post(self):
        data = api.payload
        errors = dice_schema.validate(data)
        if errors:
            return errors, 400
        quantity = data.get('quantity')
        geometry = data.get('geometry')
        discard_lowest = data.get('discard_lowest')
        if quantity is None or geometry is None:
            return {"message": "Required dice data not provided"}, 400
        return roll_dices(quantity, geometry, discard_lowest), 200


@api.route('/roll/ability', methods=['POST'])
class RollAbility(Resource):
    @ability.expect(ability_model)
    def post(self):
        data = api.payload
        errors = ability_schema.validate(data)
        if errors:
            return errors, 400

        chartype = data.get('chartype')
        attribute = data.get('ability')

        return roll_ability_scores(chartype, attribute), 200


@api.route('/roll/hp', methods=['POST'])
class RollHP(Resource):
    @hp.expect(hp_model)
    def post(self):
        data = api.payload
        errors = hp_schema.validate(data)
        if errors:
            return errors, 400
        chartype = data.get('chartype')
        conscore = data.get('conscore')
        if conscore is None:
            return {"message": "A constitution score is required"}, 400
        if chartype == 'human':
            geometry = 8
        else:
            geometry = 6
        return roll_dices(conscore, geometry, False), 200


@api.route('/roll/encounter', methods=['POST'])
class RollEncounter(Resource):
    @encounter.expect(encounter_model)
    def post(self):
        data = api.payload
        errors = encounter_schema.validate(data)
        if errors:
            return errors, 400

        terrain = data.get('terrain').lower()
        return roll_encounter(terrain), 200


@api.route('/roll/attack/mental', methods=['POST'])
class RollMentalAttack(Resource):
    @ma.expect(ma_model)
    def post(self):
        data = api.payload
        errors = ma_schema.validate(data)
        if errors:
            return errors, 400

        ams = data.get('ams')
        dms = data.get('dms')
        modifier = data.get('modifier')

        return roll_mental_attack(ams, dms, modifier), 200


@api.route('/roll/check', methods=['POST'])
class RollCheck(Resource):
    @check.expect(check_model)
    def post(self):
        data = api.payload
        errors = check_schema.validate(data)
        if errors:
            return errors, 400

        ability_score = data.get('ability_score')
        multiplier = data.get('multiplier')

        return roll_ability_check(ability_score, multiplier), 200


@api.route('/roll/tohit', methods=['GET'])
class RollToHit(Resource):
    @staticmethod
    def get():
        return roll_dices(1, 20, False), 200


@api.route('/roll/chance', methods=['GET'])
class RollChance(Resource):
    @staticmethod
    def get():
        return roll_dices(1, 100, False), 200


@api.route('/coinflip', methods=['GET'])
class RollCoinflip(Resource):
    @staticmethod
    def get():
        return random.choice(['Heads', 'Tails']), 200


@api.route('/roll/mutations', methods=['POST'])
class RollMutations(Resource):
    @mut.expect(mutation_model)
    def post(self):
        data = api.payload
        errors = mutation_schema.validate(data)
        if errors:
            return errors, 400

        conscore = data.get('conscore')
        intscore = data.get('intscore')

        return roll_mutations(conscore, intscore), 200


@api.route('/character/generate', methods=['POST'])
class GenerateCharacter(Resource):
    @character.expect(character_model)
    def post(self):
        data = api.payload
        errors = character_schema.validate(data)
        if errors:
            return errors, 400
        chartype = data.get('chartype')
        return build_character_sheet(chartype), 200


if __name__ == '__main__':
    app.run()
