_E='replace'
_D=False
_C='\n'
_B='\r\n'
_A=None
import contextlib,io,os,shlex,shutil,sys,tempfile
from .  import formatting,termui,utils
from ._compat import _find_binary_reader
class EchoingStdin:
	def __init__(A,input,output):A._input=input;A._output=output
	def __getattr__(A,x):return getattr(A._input,x)
	def _echo(A,rv):A._output.write(rv);return rv
	def read(A,n=-1):return A._echo(A._input.read(n))
	def readline(A,n=-1):return A._echo(A._input.readline(n))
	def readlines(A):return[A._echo(B)for B in A._input.readlines()]
	def __iter__(A):return iter((A._echo(B)for B in A._input))
	def __repr__(A):return repr(A._input)
def make_input_stream(input,charset):
	if hasattr(input,'read'):
		A=_find_binary_reader(input)
		if A is not _A:return A
		raise TypeError('Could not find binary reader for input stream.')
	if input is _A:input=b''
	elif not isinstance(input,bytes):input=input.encode(charset)
	return io.BytesIO(input)
class Result:
	def __init__(A,runner,stdout_bytes,stderr_bytes,exit_code,exception,exc_info=_A):A.runner=runner;A.stdout_bytes=stdout_bytes;A.stderr_bytes=stderr_bytes;A.exit_code=exit_code;A.exception=exception;A.exc_info=exc_info
	@property
	def output(self):return self.stdout
	@property
	def stdout(self):return self.stdout_bytes.decode(self.runner.charset,_E).replace(_B,_C)
	@property
	def stderr(self):
		A=self
		if A.stderr_bytes is _A:raise ValueError('stderr not separately captured')
		return A.stderr_bytes.decode(A.runner.charset,_E).replace(_B,_C)
	def __repr__(A):B=repr(A.exception)if A.exception else'okay';return f"<{type(A).__name__} {B}>"
class CliRunner:
	def __init__(A,charset='utf-8',env=_A,echo_stdin=_D,mix_stderr=True):A.charset=charset;A.env=env or{};A.echo_stdin=echo_stdin;A.mix_stderr=mix_stderr
	def get_default_prog_name(A,cli):return cli.name or'root'
	def make_env(C,overrides=_A):
		A=overrides;B=dict(C.env)
		if A:B.update(A)
		return B
	@contextlib.contextmanager
	def isolation(self,input=_A,env=_A,color=_D):
		D=env;A=self;input=make_input_stream(input,A.charset);H=sys.stdin;I=sys.stdout;J=sys.stderr;K=formatting.FORCED_WIDTH;formatting.FORCED_WIDTH=80;D=A.make_env(D);E=io.BytesIO()
		if A.echo_stdin:input=EchoingStdin(input,E)
		input=io.TextIOWrapper(input,encoding=A.charset);sys.stdout=io.TextIOWrapper(E,encoding=A.charset)
		if not A.mix_stderr:F=io.BytesIO();sys.stderr=io.TextIOWrapper(F,encoding=A.charset)
		if A.mix_stderr:sys.stderr=sys.stdout
		sys.stdin=input
		def L(prompt=_A):sys.stdout.write(prompt or'');A=input.readline().rstrip(_B);sys.stdout.write(f"{A}\n");sys.stdout.flush();return A
		def M(prompt=_A):sys.stdout.write(f"{prompt or''}\n");sys.stdout.flush();return input.readline().rstrip(_B)
		def N(echo):
			A=sys.stdin.read(1)
			if echo:sys.stdout.write(A);sys.stdout.flush()
			return A
		O=color
		def P(stream=_A,color=_A):
			A=color
			if A is _A:return not O
			return not A
		Q=termui.visible_prompt_func;R=termui.hidden_prompt_func;S=termui._getchar;T=utils.should_strip_ansi;termui.visible_prompt_func=L;termui.hidden_prompt_func=M;termui._getchar=N;utils.should_strip_ansi=P;G={}
		try:
			for (B,C) in D.items():
				G[B]=os.environ.get(B)
				if C is _A:
					try:del os.environ[B]
					except Exception:pass
				else:os.environ[B]=C
			yield(E,not A.mix_stderr and F)
		finally:
			for (B,C) in G.items():
				if C is _A:
					try:del os.environ[B]
					except Exception:pass
				else:os.environ[B]=C
			sys.stdout=I;sys.stderr=J;sys.stdin=H;termui.visible_prompt_func=Q;termui.hidden_prompt_func=R;termui._getchar=S;utils.should_strip_ansi=T;formatting.FORCED_WIDTH=K
	def invoke(B,cli,args=_A,input=_A,env=_A,catch_exceptions=True,color=_D,**G):
		C=args;E=_A
		with B.isolation(input=input,env=env,color=color)as H:
			F=_A;A=0
			if isinstance(C,str):C=shlex.split(C)
			try:I=G.pop('prog_name')
			except KeyError:I=B.get_default_prog_name(cli)
			try:cli.main(args=C or(),prog_name=I,**G)
			except SystemExit as D:
				E=sys.exc_info();A=D.code
				if A is _A:A=0
				if A!=0:F=D
				if not isinstance(A,int):sys.stdout.write(str(A));sys.stdout.write(_C);A=1
			except Exception as D:
				if not catch_exceptions:raise
				F=D;A=1;E=sys.exc_info()
			finally:
				sys.stdout.flush();K=H[0].getvalue()
				if B.mix_stderr:J=_A
				else:J=H[1].getvalue()
		return Result(runner=B,stdout_bytes=K,stderr_bytes=J,exit_code=A,exception=F,exc_info=E)
	@contextlib.contextmanager
	def isolated_filesystem(self):
		B=os.getcwd();A=tempfile.mkdtemp();os.chdir(A)
		try:yield A
		finally:
			os.chdir(B)
			try:shutil.rmtree(A)
			except OSError:pass