from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime, timedelta
from functools import wraps
import secrets, random, os, logging
from logging.handlers import RotatingFileHandler
from flasgger import Swagger
from flask_apscheduler import APScheduler

app = Flask(__name__)
app.config['SECRET_KEY'] = 'YY3_5BdticAM6E1k_3vYpg'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

class Config:
    SCHEDULER_API_ENABLED = True

app.config.from_object(Config())

scheduler = APScheduler()
scheduler.init_app(app)
scheduler.start()

def deactivate_expired_api_keys():
    with app.app_context():
        now = datetime.now()
        expired_keys = ApiKey.query.filter(ApiKey.expires_at < now, ApiKey.active == True).all()
        for key in expired_keys:
            key.active = False
        db.session.commit()

scheduler.add_job(id='Deactivate Expired Keys', func=deactivate_expired_api_keys, trigger='interval', minutes=15)

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
handler = RotatingFileHandler('moja_aplikacja.log', maxBytes=10000, backupCount=3)
logger.addHandler(handler)

class Game(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    number_to_guess = db.Column(db.Integer)
    
    def __init__(self, number_to_guess):
        self.number_to_guess = number_to_guess

class ApiKey(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    key = db.Column(db.String(80), unique=True, nullable=False)
    user_name = db.Column(db.String(100))
    expires_at = db.Column(db.DateTime)
    active = db.Column(db.Boolean, default=True)

    def __init__(self, key, user_name, expires_at=None, active=True):
        self.key = key
        self.user_name = user_name
        self.expires_at = datetime.now() + timedelta(minutes=15)
        self.active = active

def require_api_key(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        api_key = request.headers.get('API-Key')
        api_key_record = ApiKey.query.filter_by(key=api_key).first()

        if not api_key or not api_key_record or not api_key_record.active or api_key_record.expires_at < datetime.now():
            logger.warning(f"Błędny lub wygasły klucz API: {api_key}")
            return jsonify({'message': 'Brak ważnego klucza API lub klucz wygasł'}), 401
        return f(*args, **kwargs)
    return decorated_function

@app.route('/generate-api-key', methods=['POST'])
def generate_api_key():
    """
    Generuje nowy klucz API
    ---
    tags:
      - API Key Management
    responses:
      200:
        description: Wygenerowany klucz API
        schema:
          id: api_key
          properties:
            api_key:
              type: string
              description: Wygenerowany klucz API
    """
    new_key = secrets.token_urlsafe(16)
    expires_at = datetime.now() + timedelta(minutes=15)
    logger.info(f"Generowanie nowego klucza API dla użytkownika: NazwaUzytkownika")
    api_key = ApiKey(key=new_key, user_name="NazwaUzytkownika", expires_at=expires_at)
    db.session.add(api_key)
    db.session.commit()

    return jsonify({'api_key': new_key})

@app.route('/guess', methods=['POST'])
@require_api_key 
def guess_number():
    """
    Zgaduje liczbę
    ---
    tags:
      - Game
    consumes:
      - application/json
    produces:
      - application/json
    parameters:
      - in: body
        name: body
        description: Zgadywana liczba
        required: true
        schema:
          type: object
          required:
            - guess
          properties:
            guess:
              type: integer
              format: int32
              example: 25
    responses:
      200:
        description: Wynik zgadywania
        schema:
          type: object
          properties:
            message:
              type: string
            success:
              type: boolean
    """
    game = Game.query.first()
    if not game or request.json.get('new game', False):
        if game:
            db.session.delete(game)
        game = Game(random.randint(1,100))
        try:
            db.session.add(game)
            db.session.commit()
        except Exception as e:
            logger.error(f"Błąd przy zapisywaniu do bazy danych: {e}")
            

    guess = request.json.get('guess')

    if not isinstance(guess, int):
        return jsonify({'message': 'Nieprawidłowe dane. Proszę podać liczbę.'}), 400

    if guess == game.number_to_guess:
        try: 
            db.session.delete(game)
            db.session.commit()
        except Exception as e:
            logger.error(f"Błąd przy usuwaniu z bazy danych: {e}")

        return jsonify({'message': 'Gratulacje, zgadłeś!', 'success': True})
    elif guess < game.number_to_guess:
        return jsonify({'message': 'Za mało.', 'success': False})
    else:
        return jsonify({'message': 'Za dużo.', 'success': False})

Swagger(app)

if __name__ == '__main__':
     with app.app_context():
        db.create_all()
        app.run(debug=True)