from typing import Any, List, Optional
import subprocess
from fastapi import UploadFile, File, Form
from fastapi.responses import FileResponse, JSONResponse
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks, WebSocket
from sqlalchemy.orm import Session
from fastapi.encoders import jsonable_encoder
import app.crud as crud
import app.models as models
import app.schemas as schemas 
from app.api import deps

from app.core.celery_app import celery_app
from app.core.config import settings
from app.aianchor.utils2 import check_zip, VideoMakerError
from pathlib import Path
from app.db.session import SessionLocal
import requests
import asyncio

BACKEND_ZIP_STORAGE = Path("/app").joinpath(settings.BACKEND_ZIP_STORAGE)
LOCAL_ZIP_STORAGE = Path("/").joinpath(settings.LOCAL_ZIP_STORAGE)

LINE_URL = 'https://notify-api.line.me/api/notify'
LINE_TOKEN = 'o8dqdVL2k8aiWO4jy3pawZamBu53bbjoSh2u0GJ7F0j'

router = APIRouter()
video_clients = {}

@router.get("/", response_model=List[schemas.Video])
def get_video_list(
    db: Session = Depends(deps.get_db),
    skip: int = 0,
    limit: int = 100,
    current_user: models.User = Depends(deps.get_current_active_user),
) -> Any:
    """
    Retrieve items.
    """
    if crud.user.is_superuser(current_user):
        videos = crud.video.get_multi(db, skip=skip, limit=limit)
    else:
        videos = crud.video.get_multi_by_owner(
            db=db, owner_id=current_user.id, skip=skip, limit=limit
        )
    return videos

@router.post("/test")
def test(
    *,
    db: Session = Depends(deps.get_db),
    title: str,
    anchor: str,
    style: str,
    lang: str,
    current_user: models.User = Depends(deps.get_current_active_user),
) -> Any:
    video_data = {"title":title, "anchor":anchor, "lang_id":lang}
    print(video_data)
    filename = crud.video.generate_file_name(db=db, n=20)
    video_create = schemas.VideoCreate(title=title, progress_state="PENDING", stored_filename=filename)
    video = crud.video.create_with_owner(db=db, obj_in=video_create, owner_id=current_user.id, )
    return_msg = {"video_message":"accepted"}
    video_data = jsonable_encoder(video)
    video_data['membership_status'] = current_user.membership_status
    video_data['available_time'] = current_user.available_time
    video_data['video_id'] = video_data['id']
    video_data['anchor'] = anchor
    video_data['style'] = style
    video_data['lang'] = lang
    task = celery_app.send_task("app.worker.make_video_test",  kwargs=video_data)
    print(task)
    return "ok"

@router.post("/", response_model=schemas.Video)
def upload_plot(
    background_tasks: BackgroundTasks,
    *,
    db: Session = Depends(deps.get_db),
    current_user: models.User = Depends(deps.get_current_active_user),
    title: str=Form(...), 
    anchor: str=Form(...),
    style: str=Form(...),
    lang: str=Form(...),
    upload_file: UploadFile=File(),
) -> Any:
    """
    Create new video.
    """
    print(title)
    print(anchor)
    print(style)
    print(lang)
    print(upload_file.filename)
    filename = crud.video.generate_file_name(db=db, n=20)
    filepath = str(Path(BACKEND_ZIP_STORAGE).joinpath(filename+".zip"))
    try:
        with open(filepath, 'wb') as f:
            while contents := upload_file.file.read(1024 * 1024):
                f.write(contents)
    except Exception as e:
        print(e, type(e))
        error_msg = {"error_message": str(e)}
        return JSONResponse(error_msg)
    finally:
        upload_file.file.close()
    try:
      if check_zip(filepath):
        print("passed check_zip")
    except VideoMakerError as e:
      print(e)
      error_msg = {"accepted": False, "error_message":f'{e}'}
      return JSONResponse(error_msg)

    '''
    zip_filename = video.stored_file_name+".zip"
    print(str(BACKEND_ZIP_STORAGE/zip_filename))
    r = subprocess.run(["sshpass", "-p", "choozmo9", 
                    "scp", "-P", "5722", "-o", "StrictHostKeyChecking=no", f"{str(BACKEND_ZIP_STORAGE/zip_filename)}", f"root@172.104.93.163:{str(LOCAL_ZIP_STORAGE/zip_filename)}"])
    print(r.returncode)
    celery_app.send_task("app.worker.make_video", args=[video.id, video.stored_file_name, current_user.id, anchor_id, current_user.membership_status, current_user.available_time])
    '''
  
    video_create = schemas.VideoCreate(title=title, progress_state="PENDING", stored_filename=filename)
    video = crud.video.create_with_owner(db=db, obj_in=video_create, owner_id=current_user.id)
    return_msg = {"video_message":"accepted", "accepted":True}
    video_data = jsonable_encoder(video)
    video_data['membership_status'] = current_user.membership_status
    video_data['available_time'] = current_user.available_time
    video_data['video_id'] = video_data['id']
    video_data['character'] = anchor
    video_data['anchor'] = anchor
    video_data['style'] = style
    video_data['lang'] = lang
    background_tasks.add_task(wait_finish, video_data)

    headers = {
        'Authorization': 'Bearer ' + LINE_TOKEN    
    }
    data = {
        'message':f'cloud-choozmo-com\n \
                    Video\n \
                    user: {current_user.id}\n \
                    video: {video_data["id"]}\n \
                    state: queuing'     
    }
    data = requests.post(LINE_URL, headers=headers, data=data) 
    return  JSONResponse(return_msg, background=background_tasks)

async def wait_finish(video_data:dict): 
    zip_filename = video_data['stored_filename']+".zip"
    process = await asyncio.create_subprocess_exec("sshpass", "-p", "choozmo9", 
                    "scp", "-P", "5722", "-o", "StrictHostKeyChecking=no", f"{str(BACKEND_ZIP_STORAGE/zip_filename)}", f"root@172.104.93.163:{str(LOCAL_ZIP_STORAGE)}")
    await process.wait()

    task = celery_app.send_task("app.worker.make_video", kwargs=video_data)
    while True:
       await asyncio.sleep(1)
       if task.state != "PENDING":
           break
       
    db = SessionLocal()
    video = db.query(models.Video).get(video_data['id'])
    video.progress_state = "STARTED" 
    db.commit()
    db.close()
    msg_data = f"{video_data['stored_filename']}:STARTED"
    await publish(msg_data)
    headers = {
        'Authorization': 'Bearer ' + LINE_TOKEN    
    }
    data = {
        'message':f'cloud-choozmo-com\n \
                    Video\n \
                    user: {video_data["owner_id"]}\n \
                    membership: {video_data["membership_status"]}\n \
                    video: {video_data["id"]}\n \
                    state: start'     
    }
    data = requests.post(LINE_URL, headers=headers, data=data) 
    while True:
        await asyncio.sleep(1)
        if task.state != "STARTED":
            break

    if task.state == "SUCCESS":
        db = SessionLocal()
        video = db.query(models.Video).get(video_data['id'])
        user = db.query(models.User).get(video_data['owner_id'])
        video.progress_state = "SUCCESS" 
        if time := task.result:
            user.available_time -= int(time) if int(time) <= user.available_time else 0
            video.length = int(time)
        db.commit()
        db.close()
        msg_data = f"{video_data['stored_filename']}:SUCCESS:{int(time)}"
        headers = {
            'Authorization': 'Bearer ' + LINE_TOKEN    
        }
        data = {
            'message':f'cloud-choozmo-com\n \
                        Video\n \
                        user: {video_data["owner_id"]}\n \
                        membership: {video_data["membership_status"]}\n \
                        video: {video_data["id"]}\n \
                        state: success'    
        }
        data = requests.post(LINE_URL, headers=headers, data=data) 

    elif task.state == "FAILURE":
        db = SessionLocal()
        video = db.query(models.Video).get(video_data['id'])
        video.progress_state = "FAILURE" 
        db.commit()
        db.close()
        msg_data = f"{video_data['stored_filename']}:FAILURE"

        headers = {
            'Authorization': 'Bearer ' + LINE_TOKEN   
        }
        data = {
            'message':f'cloud-choozmo-com\n \
                        Video\n \
                        user: {video_data["owner_id"]}\n \
                        membership: {video_data["membership_status"]}\n \
                        video: {video_data["id"]}\n \
                        state: failure'     
        }
        data = requests.post(LINE_URL, headers=headers, data=data) 

    await publish(msg_data)

async def publish(data):
    for video_client in video_clients.values():
        await video_client.send_text(f"{data}")

@router.get("/{id}")
def download_video(
    *,
    db: Session = Depends(deps.get_db),
    id: int,
    current_user: models.User = Depends(deps.get_current_active_user),
) -> Any:
    
    return {"message":"address"}

@router.websocket("")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    key = websocket.headers.get('sec-websocket-key')
    video_clients[key] = websocket
    try:
        while True:
            data = await websocket.receive_text()
            if not data.startswith("subscribe"):
              del video_clients[key]
              #for client in sr_clients.values():
              #      await client.send_text(f"ID: {key} | Message: {data}")

    except:
        # 接続が切れた場合、当該クライアントを削除する
        del video_clients[key]
  
# # mia write  
# from pydantic import BaseModel
        
# class phone_json(BaseModel):
#     title : str
#     text : str

# IMAGEDIR = "/tmp/"
# IMAGEDIR_all = "/var/lib/docker/overlay2/1e9b63ea29cc42551035ae2c5ce1cdf0c44a3f254879175a8a37db08ac7154de/diff/tmp/"


# @router.post("/phone_input_json")
# async def phone_input_json(
#      input_model:List[UploadFile],
#      text: str
# ):
#     try:
#         result = []
        
#         text_json = eval(text)
        
#         for item,text_tmp in zip(input_model,text_json):
        
#             # img_url = ''
#             # if item != '':
#             #    try:
#             #        contents = await item.read()
        
#                     #save the file
#             #        with open(f"{IMAGEDIR}{item.filename}", "wb") as f:
#             #            f.write(contents)
                
#             #        img_url = f"{IMAGEDIR_all}{item.filename}"
#             #    except Exception as e:
#             #         img_url= str(e)
        
#             result.append(
#                 {
#                     "img" : item,
#                     "title" :text_tmp["title"],
#                     "text" : str(text_tmp["text"])
#                 }
#             )
            
#         return result
#     except Exception as e:
#         return {"msg": str(e), "code": 500} 
