# coding: utf-8
import hashlib
import os
from typing import Dict, List  # noqa: F401

from fastapi import (  # noqa: F401
    APIRouter,
    Body,
    Cookie,
    Depends,
    Form,
    Header,
    Path,
    Query,
    Response,
    Security,
    status,
    HTTPException,
)

from sqliteclient.data.Models import TablePasswords
from . import sqliteClient
from server.models.extra_models import TokenModel  # noqa: F401

from server.data.definitions import API_SUB_PATH, ErrorTypes, Messages
from server.models.change_password_post201_response_inner import ChangePasswordPost201ResponseInner
from server.models.error import Error
from server.models.password_change_model import PasswordChangeModel
from server.security_api import get_token_ApiKeyAuth

router = APIRouter(prefix=API_SUB_PATH)


@router.put(
    "/change-password",
    responses={
        201: {"model": List[ChangePasswordPost201ResponseInner], "description": "New password was set successfully."},
        400: {"model": Error, "description": "Bad request."},
        401: {"model": Error, "description": "Password is wrong."},
        404: {"model": Error, "description": "User not found."},
    },
    tags=["accountManagement"],
    response_model_by_alias=True,
)
async def change_password_put(
    password_change_model: PasswordChangeModel = Body(None, description="Requestbody for the PUT action."),
    token_ApiKeyAuth: TokenModel = Security(get_token_ApiKeyAuth),
) -> List[ChangePasswordPost201ResponseInner]:
    """Password change."""
    # -------------------------------------------- Check fields aren't empty ------------------------------------------

    if password_change_model is None:
        errorResponse = Error(
            status=status.HTTP_400_BAD_REQUEST,
            type=ErrorTypes.REQUEST_IS_EMPTY.value,
            message=Messages.REQUEST_IS_EMPTY.value,
        )
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=errorResponse.dict())

    # -------------------------------------------- Load database --------------------------------------------

    # Load data from the database into the client, if this didn't happen
    if not sqliteClient.wasLoaded:
        sqliteClient.startLoadTablesTask()
        await sqliteClient.loadTablesTask

    # -------------------------------------------- User authentication --------------------------------------------

    # Check if the user does exist
    if not sqliteClient.checkUserExists(password_change_model.username):
        errorResponse = Error(
            status=status.HTTP_404_NOT_FOUND,
            type=ErrorTypes.USER_NOT_FOUND.value,
            message=Messages.USER_NOT_FOUND.value,
        )
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=errorResponse.dict())

    # Check if the password does match
    if not sqliteClient.checkPassword(password_change_model.username, password_change_model.oldpassword):
        errorResponse = Error(
            status=status.HTTP_401_UNAUTHORIZED, type=ErrorTypes.WRONG_PASS.value, message=Messages.PASS_NOT_MATCH.value
        )
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=errorResponse.dict())

    # -------------------------------------------- Create the new password --------------------------------------------

    seed = os.urandom(32)

    userData = sqliteClient.getUsersPasswords(whereUser=password_change_model.username)[0]

    # Concatenate the hashed password and the seed
    dataToHash = password_change_model.newpassword.encode("utf-8") + seed

    # Hash the concatenated data using SHA256
    finalHash = hashlib.sha256(dataToHash).hexdigest()

    newEntry = TablePasswords(
        passwordId=userData.passwordId, users_userId=userData.userId, password=finalHash, seed=seed.hex()
    )

    await sqliteClient.setPassword(newEntry)

    return [ChangePasswordPost201ResponseInner(status="Password successfully created.")]
