from pathlib import Path
import sys
import os
import subprocess

def _is_vasp4_poscar(path: str) -> bool:
    try:
        with open(path, "r") as f:
            lines = []
            for _ in range(7):
                line = f.readline()
                if not line:
                    break
                lines.append(line)
        if len(lines) < 6:
            return False
        line6 = lines[5].strip().split()
        if not line6:
            return False
        return all(p.isdigit() for p in line6)
    except Exception:
        return False

def _is_cif(path: str) -> bool:
    p = Path(path)
    if p.suffix.lower() == ".cif":
        return True
    try:
        with open(path, "r") as f:
            for _ in range(5):
                line = f.readline()
                if not line:
                    break
                if line.strip().lower().startswith("data_"):
                    return True
    except Exception:
        pass
    return False

def _expand_paths(paths):
    import glob
    out = []
    for pat in paths:
        if Path(pat).exists():
            out.append(pat)
        else:
            out.extend(glob.glob(pat))
    return sorted(list(dict.fromkeys(out)))

def _download_poscar_from_mp(mpid=None, formula=None, workdir=None, silent=True):
    if not (mpid or formula):
        raise ValueError("mpid or formula is required.")
    code = (
        "from argparse import Namespace\n"
        "from vise.cli.main_functions import get_poscar_from_mp\n"
        f"args=Namespace(mpid={mpid!r}, formula={formula!r})\n"
        "get_poscar_from_mp(args)\n"
    )
    cwd = workdir if workdir else None
    run_kwargs = dict(cwd=cwd, env=os.environ.copy(), text=True)
    if silent:
        run_kwargs["stdout"] = subprocess.DEVNULL
        run_kwargs["stderr"] = subprocess.DEVNULL
        result = subprocess.run([sys.executable, "-c", code], **run_kwargs)
    else:
        run_kwargs["stdout"] = subprocess.PIPE
        run_kwargs["stderr"] = subprocess.PIPE
        result = subprocess.run([sys.executable, "-c", code], **run_kwargs)

        def _filter_info_lines(s: str) -> str:
            if not s:
                return s
            lines = s.splitlines()
            filtered = []
            skip_exact = {
                "INFO: -- Settings from vise.yaml:",
                "INFO:",
                "INFO: Note that you're using the newer MPRester.",
            }
            for line in lines:
                if line.strip() in skip_exact:
                    continue
                filtered.append(line)
            return "\n".join(filtered) + ("\n" if s.endswith("\n") else "")

        if result.stdout:
            sys.stdout.write(_filter_info_lines(result.stdout))
        if result.stderr:
            sys.stderr.write(_filter_info_lines(result.stderr))
    if result.returncode != 0:
        raise RuntimeError("MP download process failed.")

def resolve_structure_inputs(poscar_paths=None, cif_paths=None, formula=None, mpid=None, workdir=None, show_mp_progress=False):
    """
    Resolve structure inputs with priority:
      1) poscar_paths (POSCAR or CIF)
      2) cif_paths
      3) mpid/formula via MP download
    Returns list of POSCAR paths.
    """
    if poscar_paths:
        in_paths = _expand_paths(poscar_paths)
        if not in_paths:
            print(f"No input files found matching: {poscar_paths}")
            sys.exit(1)
    elif cif_paths:
        in_paths = _expand_paths(cif_paths)
        if not in_paths:
            print(f"No input files found matching: {cif_paths}")
            sys.exit(1)
    elif mpid or formula:
        # Dependency check is deferred to the subprocess import
        if workdir:
            Path(workdir).mkdir(parents=True, exist_ok=True)
            cwd = Path.cwd()
            try:
                os.chdir(workdir)
                try:
                    _download_poscar_from_mp(mpid=mpid, formula=formula, workdir=".", silent=not show_mp_progress)
                except Exception as e:
                    print("Error: Failed to retrieve POSCAR from Materials Project.")
                    print(f"Details: {e}")
                    print("Please provide a local POSCAR/CIF via -p/--poscar instead.")
                    sys.exit(1)
            finally:
                os.chdir(cwd)
        else:
            try:
                _download_poscar_from_mp(mpid=mpid, formula=formula, workdir=None, silent=not show_mp_progress)
            except Exception as e:
                print("Error: Failed to retrieve POSCAR from Materials Project.")
                print(f"Details: {e}")
                print("Please provide a local POSCAR/CIF via -p/--poscar instead.")
                sys.exit(1)
        in_paths = ["POSCAR"]
    else:
        return []

    poscar_out = []
    for p in in_paths:
        if _is_cif(p):
            try:
                from ase.io import read, write
            except Exception as e:
                print(f"Error: ASE required to read CIF ({p}): {e}")
                sys.exit(1)
            atoms = read(p)
            out_path = str(Path(p).with_suffix(".POSCAR"))
            write(out_path, atoms, format="vasp")
            poscar_out.append(out_path)
            continue

        if _is_vasp4_poscar(p):
            print("Error: VASP4 POSCAR detected.")
            print("Please convert to VASP5 by adding element symbols on line 6, then retry.")
            sys.exit(1)
        poscar_out.append(p)
    return poscar_out

def vasp4to5(input_path: str, symbols: str = None, output_path: str = None):
    """
    Convert VASP4 POSCAR to VASP5 by adding element symbols.
    """
    in_file = Path(input_path)
    if not in_file.exists():
        print(f"Error: File '{input_path}' not found.")
        return
    
    if output_path is None:
        output_path = f"{input_path}_v5"

    try:
        with open(in_file, 'r') as f:
            lines = f.readlines()
        
        if len(lines) < 6:
            print("Error: Invalid POSCAR file (too short).")
            return

        # Check if it's already VASP5
        line6 = lines[5].strip().split()
        if not all(p.isdigit() for p in line6):
            print(f"Info: '{input_path}' already appears to have element symbols (VASP5?).")
            # We can still proceed if symbols are provided to override
        
        if symbols is None:
            # Try to guess from the first line (comment)
            guess = lines[0].strip().split()
            print(f"No symbols provided. Guessing from comment line: {' '.join(guess)}")
            symbols = " ".join(guess)
            # Basic validation: symbols should not be digits
            if any(s.isdigit() for s in guess):
                print("Error: Could not guess symbols safely. Please provide them with -s \"El1 El2 ...\"")
                return

        # VASP5 structure:
        # 1. Comment
        # 2. Scale
        # 3-5. Lattice
        # 6. Element Symbols (NEW)
        # 7. Atom counts
        # ...
        
        new_lines = lines[:5]
        new_lines.append(f" {symbols}\n")
        new_lines.extend(lines[5:])
        
        with open(output_path, 'w') as f:
            f.writelines(new_lines)
        
        print(f"Successfully converted VASP4 -> VASP5: {output_path}")
        print(f"Added element line: {symbols}")

    except Exception as e:
        print(f"Error during conversion: {e}")
