import shutil
import pandas as pd
from pathlib import Path
import zipfile
from io import BytesIO
from chardet.universaldetector import UniversalDetector
import pandas as pd
try:
  from app.aianchor.config import *
except ImportError:
  from config import *

DEFAULT_ENCODING = "utf-8"

class VideoMakerError(Exception):
  pass

def json2table(filename: str):
  table = pd.read_json(filename)
  print(table)
  
def guess_codec(filenames: list) -> str:
  codec_detector = UniversalDetector()
  for filename in filenames:
    codec_detector.feed(filename.encode('cp437'))
    if codec_detector.done:
      break

  result = codec_detector.close()
  encoding = result.get("encoding")
  return encoding or DEFAULT_ENCODING

def check_zip(zip_filepath:str):
  path = Path(zip_filepath)
  with zipfile.ZipFile(str(path)) as zf:
    filenames = [x for x in zf.namelist() if not x.endswith('/')]
    result = guess_codec(filenames)
    true_filepaths = [x.encode('cp437').decode(result) for x in zf.namelist() if not x.endswith('/')]
    scenarios_files = [(Path(x).name, i) for i, x in enumerate(true_filepaths) if Path(x).suffix in [".xlsx", ".csv"] and not Path(x).name.startswith("._") and not Path(x).name.startswith("~$") and Path(x).stem != "style"]
    print(scenarios_files)
    
    if len(scenarios_files) == 0:
      raise VideoMakerError("no excel or csv file in zip.")
    if len(scenarios_files) > 1:
      raise VideoMakerError("too many excel or csv file in zip.")
    f = zf.read(filenames[scenarios_files[0][1]])
    if Path(scenarios_files[0][0]).suffix == ".xlsx":
      table = pd.read_excel(BytesIO(f), dtype=object)
    elif Path(scenarios_files[0][0]).suffix == ".csv":
      table = pd.read_csv(BytesIO(f), dtype=object)
    table.reset_index(inplace=True)
    
    true_filenames = [(Path(x).name, i) for i, x in enumerate(true_filepaths)]
    true_stems = [(Path(x).stem, i) for i, x in enumerate(true_filepaths)]
    for i in range(len(table)):
      # excel 裡的圖檔跟zip裡的檔案要一致
      if not table.loc[i, ['素材']].isna().item():
        img =  table.loc[i, ['素材']].item()
        img = str(img)
        img_files = [x.strip() for x in img.split(',')]
        for img in img_files:
          if Path(img).suffix:
            n = len([x for x in true_filenames if x[0] == img])
          else:
            n = len([x for x in true_stems if x[0] == img])
          if n == 0:
            raise VideoMakerError(f"{img}: no such img file in zip.")
          elif n > 1:
            raise VideoMakerError(f'too many same name img files as {img} in zip')

      # 需要tts文字或音檔
      if '字幕' in table.columns and table.loc[i, ['字幕']].isna().item():
        if '音檔' in table.columns and table.loc[i, ['音檔']].isna().item():
          raise VideoMakerError(f'text or voice file is needed at scene {i+1}.')
        voice_file = table.loc[i, ['音檔']].item()
        if Path(voice_file).suffix:
          n = len([x for x in true_filenames if x[0]==voice_file])
        else:
          n = true_stems.count(voice_file)
        if n == 0:
          raise VideoMakerError(f"{voice_file}: no such voice file in zip.")
        elif n > 1:
          raise VideoMakerError(f'too many same name voice files as {voice_file} in zip')
  
  return True

def prepare_workingdir(work_dir_path:str):
  work_dir_Path = Path(work_dir_path)
  (work_dir_Path/voice_dir).mkdir(exist_ok=True)
  (work_dir_Path/caption_dir).mkdir(exist_ok=True)
  (work_dir_Path/input_dir).mkdir(exist_ok=True)
  (work_dir_Path/output_dir).mkdir(exist_ok=True)
  (work_dir_Path/srt_dir).mkdir(exist_ok=True)
  (work_dir_Path/ai_character_dir).mkdir(exist_ok=True)

def check_and_extract_zip(zip_filepath:str, working_dirpath:str):
  working_dir_Path = Path(working_dirpath)
  with zipfile.ZipFile(zip_filepath) as zf:
    filenames = [x for x in zf.namelist() if not x.endswith('/')]
    result = guess_codec(filenames)
    true_filepaths = [x.encode('cp437').decode(result) for x in zf.namelist() if not x.endswith('/')]
    scenarios_filenames = [(Path(x).name, i) for i, x in enumerate(true_filepaths) if Path(x).suffix in [".xlsx", ".csv"] and not Path(x).name.startswith("._") and not Path(x).name.startswith("~$") and Path(x).stem != "style" ]
    media_filenames = [(Path(x).name, i) for i, x in enumerate(true_filepaths) if Path(x).suffix not in [".xlsx", ".csv"] and not Path(x).name.startswith("._") and not Path(x).name.startswith("~$")]
    
    if len(scenarios_filenames) == 0:
      raise VideoMakerError("no excel or csv file in zip.")
    if len(scenarios_filenames) > 1:
      raise VideoMakerError("too many excel or csv file in zip.")
    
    save_filepath = working_dir_Path / input_dir / scenarios_filenames[0][0]
    if not save_filepath.exists():
      with open(save_filepath, 'wb') as output_file:  # 建立並開啟新檔案
        with zf.open(filenames[scenarios_filenames[0][1]], 'r') as origin_file:  # 開啟原檔案
          shutil.copyfileobj(origin_file, output_file)  # 將原檔案內容複製到新檔案 
    else:
      raise VideoMakerError(f"{scenarios_filenames[0]} already exists.")
    
    f = zf.read(filenames[scenarios_filenames[0][1]])
    if Path(scenarios_filenames[0][0]).suffix == ".xlsx":
      table = pd.read_excel(BytesIO(f), dtype=object)
    elif Path(scenarios_filenames[0][0]).suffix == ".csv":
      table = pd.read_csv(BytesIO(f), dtype=object)
    table.reset_index(inplace=True)
    
    true_filenames = [(Path(x).name, i) for i, x in enumerate(true_filepaths)]
    true_stems = [(Path(x).stem, i) for i, x in enumerate(true_filepaths)]
    for i in range(len(table)):
      # excel 裡的圖檔跟zip裡的檔案要一致
      if not table.loc[i, ['素材']].isna().item():
        img =  table.loc[i, ['素材']].item()
        img_files = [x.strip() for x in img.split(',')]
        for img in img_files:
          if Path(img).suffix:
            target_filenames = [x for x in true_filenames if x[0] == img]
          else:
            target_filenames = [x for x in true_stems if x[0] == img]
          if len(target_filenames) == 0:
            raise VideoMakerError(f"{img}: no such img file in zip.")
          elif len(target_filenames) > 1:
            raise VideoMakerError(f'too many same name img files as {img} in zip')
          
          save_filepath = working_dir_Path / input_dir / img_dir / true_filenames[target_filenames[0][1]][0]
          if not save_filepath.exists():
            with open(save_filepath, 'wb') as output_file:  # 建立並開啟新檔案
              with zf.open(filenames[target_filenames[0][1]], 'r') as origin_file:  # 開啟原檔案
                shutil.copyfileobj(origin_file, output_file)  # 將原檔案內容複製到新檔案
          else:
            raise VideoMakerError(f"{target_filenames[0][0]} already exists.")
        
      # 需要tts文字或音檔
      if '字幕' in table.columns and table.loc[i, ['字幕']].isna().item():
        if '音檔' in table.columns and table.loc[i, ['音檔']].isna().item():
          raise VideoMakerError(f'text or voice file is needed at scene {i+1}.')
        voice_file = table.loc[i, ['音檔']].item()
        if Path(voice_file).suffix:
          target_filenames = [x for x in true_filenames if x[0] == voice_file]
        else:
          target_filenames = [x for x in true_stems if x[0] == voice_file]
        if len(target_filenames) == 0:
          raise VideoMakerError(f"{voice_file}: no such voice file in zip.")
        elif len(target_filenames) > 1:
          raise VideoMakerError(f'too many same name voice files as {voice_file} in zip')
        
        save_filepath = working_dir_Path / input_dir / true_filenames[target_filenames[0][1]][0]
        if not save_filepath.exists():
          with open(save_filepath, 'wb') as output_file:  # 建立並開啟新檔案
            with zf.open(filenames[target_filenames[0][1]], 'r') as origin_file:  # 開啟原檔案
              shutil.copyfileobj(origin_file, output_file)  # 將原檔案內容複製到新檔案
        else:
          raise VideoMakerError(f"{target_filenames[0][0]} already exists.")
        
if __name__ == "__main__":
  from fire import Fire
  
  def fire_check_zip(zip_filepath:str):
    try:
      if check_zip(zip_filepath):
        print("passed check_zip")
    except VideoMakerError as e:
      print(e)
  
  Fire(fire_check_zip)