from flask import Flask, request,render_template
from flask import jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_restful import Resource, Api
from flasgger import Swagger, swag_from
import os

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///notes_api.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SWAGGER'] = {'title': 'Notes API', 'uiversion': 3}
swagger = Swagger(app)

db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
api = Api(app)

# Home page route
@app.route('/')
def home():
    return '''
    <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Notes API</title>
</head>
<body>
    <h1>Notes API</h1>
    <p>Welcome to the Notes API! This API allows you to manage user accounts and their associated notes. With this API, you can:</p>
    <ul>
        <li>Register new users</li>
        <li>Login with existing users</li>
        <li>Create notes for a specific user</li>
        <li>Retrieve all notes for a specific user</li>
        <li>Search for notes containing specific keywords</li>
    </ul>
    <p>For more details on how to use the API, please visit the <a href="/apidocs">API documentation</a>.</p>

    <h2>Using the Swagger Page</h2>
    <p>Follow the steps below to interact with the API using the Swagger page:</p>
    <ol>
        <li>Open the API documentation by navigating to <a href="/apidocs" target="_blank">/apidocs</a> in your web browser.</li>
        <li>Explore the available API endpoints by expanding each section. You will find information about the HTTP methods, required parameters, and request/response data.</li>
        <li>To test an endpoint, click on the "Try it out" button for that specific endpoint.</li>
        <li>Enter any required parameters or request body data in the provided input fields.</li>
        <li>Click "Execute" to send the request. The response will appear below the "Execute" button, including the response status, headers, and body.</li>
        <li>Use the information on the Swagger page to learn about and test different API endpoints in your application.</li>
    </ol>

    <h2>Interacting with the API using Postman</h2>
    <p>Follow the steps below to interact with the API using Postman:</p>
    <ol>
        <li>Download and install <a href="https://www.postman.com/downloads/" target="_blank">Postman</a> on your computer.</li>
        <li>Launch Postman and click on "Create a request" or use the "+" button to create a new request tab.</li>
        <li>Select the appropriate HTTP method (GET, POST, PUT, DELETE) from the dropdown menu next to the address bar.</li>
        <li>Enter the API endpoint URL (e.g., http://172.105.55.98:502/register for registering a new user).</li>
        <li>If the request requires a request body (e.g., POST requests), select the "Body" tab and choose "raw" and "JSON" as the format. Enter the required JSON data in the text area below.</li>
        <li>Click "Send" to make the request. The response will appear in the "Response" section below the request.</li>
    </ol>
</body>
</html>
    '''
# User model
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password = db.Column(db.String(120), nullable=False)

    def __repr__(self):
        return f'<User {self.username}>'

# Note model
class Note(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(80), nullable=False)
    content = db.Column(db.Text, nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)

    def __repr__(self):
        return f'<Note {self.title}>'

db.create_all()

# User registration
class Register(Resource):
    @swag_from({
        'tags': ['Authentication'],
        'definitions': {'User': {'type': 'object', 'properties': {'username': {'type': 'string'}, 'password': {'type': 'string'}}}},
        'responses': {'201': {'description': 'User registered successfully'}, '400': {'description': 'Missing username or password'}},
        'parameters': [{'in': 'body', 'name': 'body', 'description': 'User object that needs to be registered', 'required': True, 'schema': {'$ref': '#/definitions/User'}}]
    })
    def post(self):
        data = request.get_json()
        username = data.get('username')
        password = data.get('password')

        if not username or not password:
            return {"message": "Missing username or password"}, 400

        hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')

        new_user = User(username=username, password=hashed_password)
        db.session.add(new_user)
        db.session.commit()

        return {"message": "User registered successfully"}, 201

# User login
class Login(Resource):
    @swag_from({
        'tags': ['Authentication'],
        'definitions': {'User': {'type': 'object', 'properties': {'username': {'type': 'string'}, 'password': {'type': 'string'}}}},
        'responses': {'200': {'description': 'Logged in successfully'}, '401': {'description': 'Invalid username or password'}},
        'parameters': [{'in': 'body', 'name': 'body', 'description': 'User object that needs to log in', 'required': True, 'schema': {'$ref': '#/definitions/User'}}]
    })
    def post(self):
        data = request.get_json()
        username = data.get('username')
        password = data.get('password')

        user = User.query.filter_by(username=username).first()
        if user and bcrypt.check_password_hash(user.password, password):
            return {"message": "Logged in successfully", "user_id": user.id}
        else:
            return {"message": "Invalid username or password"}, 401

# Note creation
class CreateNote(Resource):
    @swag_from({
        'tags': ['Notes'],
        'definitions': {'Note': {'type': 'object', 'properties': {'user_id': {'type': 'integer'}, 'title': {'type': 'string'}, 'content': {'type': 'string'}}}},
        'responses': {'201': {'description': 'Note created successfully'}, '400': {'description': 'Missing user_id, title or content'}},
        'parameters': [{'in': 'body', 'name': 'body', 'description': 'Note object that needs to be created', 'required': True, 'schema': {'$ref': '#/definitions/Note'}}]
    })
    def post(self):
        data = request.get_json()
        user_id = data.get('user_id')
        title = data.get('title')
        content = data.get('content')

        if not user_id or not title or not content:
            return jsonify({"message": "Missing user_id, title, or content"}), 400

        new_note = Note(user_id=user_id, title=title, content=content)
        db.session.add(new_note)
        db.session.commit()

        return {"message": "Note created successfully"}, 201

# Note retrieval
class GetNotes(Resource):
    @swag_from({
        'tags': ['Notes'],
        'responses': {'200': {'description': 'Notes retrieved successfully'}},
        'parameters': [{'in': 'path', 'name': 'user_id', 'type': 'integer', 'description': 'User ID whose notes need to be retrieved', 'required': True}]
    })
    def get(self, user_id):
        notes = Note.query.filter_by(user_id=user_id).all()
        notes_list = [{"id": note.id, "title": note.title, "content": note.content} for note in notes]
        return jsonify({"notes": notes_list})

# Note search
class SearchNotes(Resource):
    @swag_from({
        'tags': ['Notes'],
        'responses': {'200': {'description': 'Notes retrieved successfully'}},
        'parameters': [{'in': 'path', 'name': 'user_id', 'type': 'integer', 'description': 'User ID whose notes need to be searched', 'required': True},
                       {'in': 'query', 'name': 'query', 'type': 'string', 'description': 'Search term to filter notes', 'required': True}]
    })
    def get(self, user_id):
        search_query = request.args.get('query', '')
        notes = Note.query.filter(Note.user_id == user_id, Note.title.contains(search_query)).all()
        notes_list = [{"id": note.id, "title": note.title, "content": note.content} for note in notes]
        return jsonify({"notes": notes_list})

api.add_resource(Register, '/register')
api.add_resource(Login, '/login')
api.add_resource(CreateNote, '/create_note')
api.add_resource(GetNotes, '/get_notes/<int:user_id>')
api.add_resource(SearchNotes, '/search_notes/<int:user_id>')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=502)
