import {recordLoginContext} from './login-context-server';
import {boundedUserAgent} from './login-context';
import {AccountAccessError,readAccountAccess,requireAccountAccess} from './account-access-server';
import {accessRestriction,type AccountAccess} from './account-access';
import {configured,env,json,readJson,sameOrigin} from './server';
import {normalizeEmail,normalizePhone,type CustomerUser} from './auth-input';
import {type AuthUser,type AuthSession,isAuthUser,isAuthSession,setSession,cookieValue,clearSession} from './auth-cookies';

export class AuthError extends Error {status:number;constructor(message:string,status=400){super(message);this.status=status}}
const hookFailureCodes=new Set(['hook_timeout','hook_timeout_after_retry','hook_payload_invalid_content_type','hook_payload_over_size_limit']);
export async function authRequest<T=Record<string,unknown>>(path:string,body?:unknown,token?:string,method=body===undefined?'GET':'POST',clientRequest?:Request){
 if(!configured())throw new AuthError('سرویس ورود در حال حاضر در دسترس نیست.',503);
 const result=await fetch(`${env('SUPABASE_URL').replace(/\/$/,'')}/auth/v1/${path}`,{
  method,cache:'no-store',headers:{apikey:env('SUPABASE_PUBLISHABLE_KEY'),'Content-Type':'application/json',...(clientRequest?{'User-Agent':boundedUserAgent(clientRequest.headers.get('user-agent'))}:{}),...(token?{Authorization:`Bearer ${token}`}:{})},
  ...(body===undefined?{}:{body:JSON.stringify(body)}),signal:AbortSignal.timeout(12000)
 });
 const raw=await result.json().catch(()=>({}));const data=(raw&&typeof raw==='object'?raw:{}) as T;return {result,data};
}
export function assertAuthResult(result:Response,data:unknown,fallback:string){
 if(result.ok)return;
 const details=data as {code?:string;error_code?:string}|null;const code=details?.error_code||details?.code;
 if(result.status===429||code==='over_sms_send_rate_limit')throw new AuthError('تعداد تلاش‌ها بیش از حد مجاز است. کمی صبر کنید و دوباره تلاش کنید.',429);
 if(code==='invalid_credentials')throw new AuthError('شناسهٔ ورود یا رمز عبور نادرست است. اطلاعات ذخیره‌شده را بررسی کنید یا از «فراموشی رمز عبور» استفاده کنید.',400);
 if(code==='user_banned')throw new AuthError('ورود به این حساب غیرفعال است. برای پیگیری با پشتیبانی تماس بگیرید.',403);
 if(code==='phone_provider_disabled'||code==='sms_send_failed'||code==='sms_send_hook_not_configured')throw new AuthError('ارسال پیامک در حال حاضر در دسترس نیست. از ورود با رمز عبور استفاده کنید.',503);
 if(code==='weak_password')throw new AuthError('رمز قوی‌تری انتخاب کنید؛ دست‌کم ۱۲ نویسه و ترکیبی از حروف، عدد و نشانه‌ها.',400);
 if(hookFailureCodes.has(code||'')||result.status>=500)throw new AuthError('سرویس ورود در حال حاضر در دسترس نیست. لطفاً بعداً دوباره تلاش کنید.',503);
 throw new AuthError(fallback,400);
}
export async function authBody(request:Request){
 if(!sameOrigin(request))throw new AuthError('درخواست نامعتبر است.',403);
 if(!request.headers.get('content-type')?.includes('application/json'))throw new AuthError('درخواست نامعتبر است.',415);
 if(!configured())throw new AuthError('سرویس ورود در حال حاضر در دسترس نیست.',503);
 let body:unknown;try{body=await readJson(request,16000)}catch{throw new AuthError('اطلاعات ارسال‌شده معتبر نیست.')}
 if(!body||typeof body!=='object'||Array.isArray(body))throw new AuthError('اطلاعات ارسال‌شده معتبر نیست.');
 return body as Record<string,unknown>;
}
export function authFailure(error:unknown){return json({error:error instanceof AuthError||error instanceof AccountAccessError?error.message:'ارتباط برقرار نشد. دوباره تلاش کنید.',...(error instanceof AccountAccessError?{code:error.code}: {})},error instanceof AuthError||error instanceof AccountAccessError?error.status:503)}
export async function phoneReady(){
 const {result,data}=await authRequest<{external?:{phone?:boolean};phone_autoconfirm?:boolean}>('settings');
 if(!result.ok||!data.external?.phone||data.phone_autoconfirm!==false)throw new AuthError('تأیید پیامکی در حال حاضر در دسترس نیست. لطفاً از ورود با رمز عبور استفاده کنید.',503);
}
export function publicUser(user:AuthUser):CustomerUser{
 const metadata=user.user_metadata||{};
 const name=[metadata.first_name,metadata.last_name].filter(v=>typeof v==='string').join(' ').slice(0,161);
 return {id:user.id,email:user.email||null,phone:user.phone||null,name};
}
export async function customerUser(user:AuthUser,token:string):Promise<CustomerUser>{
 const result=publicUser(user);
 try{
  const response=await fetch(`${env('SUPABASE_URL')}/rest/v1/rpc/marketplace_my_username`,{method:'POST',cache:'no-store',headers:{apikey:env('SUPABASE_PUBLISHABLE_KEY'),Authorization:`Bearer ${token}`,'Content-Type':'application/json'},body:'{}',signal:AbortSignal.timeout(5000)});
  if(response.ok){const username=await response.json();if(typeof username==='string')result.username=username}
 }catch{/* A profile lookup failure must not invalidate an authenticated session. */}
 return result;
}
export async function loginResponse(request:Request,data:unknown,remember:boolean,notice?:string,trackLogin=true){
 if(!isAuthSession(data))throw new AuthError('ورود کامل نشد. دوباره تلاش کنید.',503);
 let access:AccountAccess;
 try{access=await requireAccountAccess(data.access_token,'login',data.user.id)}catch(error){
  try{await authRequest('logout?scope=local',{},data.access_token)}catch{}
  const response=authFailure(error);clearSession(response,request);return response;
 }
 if(trackLogin){const recorded=await recordLoginContext(request,data.access_token);if(!recorded)console.warn('Login context could not be recorded; authentication succeeded.')}
 const response=json({user:{...await customerUser(data.user,data.access_token),access},notice});setSession(response,request,data,remember);return response;
}
export async function resolveUsername(username:string):Promise<{email:string}|{phone:string}>{
 const token=env('AUTH_DIRECTORY_TOKEN'),url=env('AUTH_DIRECTORY_URL');
 if(!token||!url)throw new AuthError('ورود با نام کاربری هنوز فعال نیست. ایمیل یا شماره موبایل را وارد کنید.',503);
 if(!url.startsWith('https://'))throw new AuthError('ورود با نام کاربری موقتاً در دسترس نیست.',503);
 const response=await fetch(url,{method:'POST',cache:'no-store',headers:{Authorization:`Bearer ${token}`,'Content-Type':'application/json'},body:JSON.stringify({username}),signal:AbortSignal.timeout(10000)});
 if(!response.ok)throw new AuthError('ورود با نام کاربری موقتاً در دسترس نیست. ایمیل یا شماره موبایل را وارد کنید.',503);
 const data=await response.json() as {phone?:unknown;email?:unknown}|null;
 const phone=normalizePhone(data?.phone);if(phone)return {phone};const email=normalizeEmail(data?.email);if(email)return {email};
 // Unknown usernames still take the password grant path and return the same error.
 return {email:`unknown-${crypto.randomUUID()}@invalid.invalid`};
}
const refreshing=new Map<string,Promise<AuthSession|null>>();
export async function refreshSession(refresh:string){
 const pending=refreshing.get(refresh);if(pending)return pending;
 const promise=(async()=>{
  const {result,data}=await authRequest('token?grant_type=refresh_token',{refresh_token:refresh});
  if(result.status>=500||result.status===429)throw new AuthError('ارتباط با حساب شما موقتاً برقرار نشد.',503);
  return result.ok&&isAuthSession(data)?data:null;
 })();
 refreshing.set(refresh,promise);try{return await promise}finally{refreshing.delete(refresh)}
}
export async function sessionResponse(request:Request){
 const token=cookieValue(request,'planck_session');
 if(token){
  const {result,data}=await authRequest('user',undefined,token);
  if(result.ok&&isAuthUser(data)){
   const access=await readAccountAccess(token,data.id);
   if(access.status!=='active'){
    try{await authRequest('logout?scope=local',{},token)}catch{}
    const response=json({user:null,notice:accessRestriction(access),access});clearSession(response,request);return response;
   }
   return json({user:{...await customerUser(data,token),access}});
  }
  if(result.status!==401&&result.status!==403)throw new AuthError('ارتباط با حساب شما موقتاً برقرار نشد.',503);
 }
 const refresh=cookieValue(request,'planck_refresh');
 if(refresh){const session=await refreshSession(refresh);if(session)return loginResponse(request,session,cookieValue(request,'planck_remember')==='1',undefined,false)}
 const response=json({user:null});clearSession(response,request);return response;
}

export async function activeCustomerSession(request:Request){
 if(request.headers.get('sec-fetch-site')==='cross-site')throw new AuthError('درخواست نامعتبر است.',403);
 const token=cookieValue(request,'planck_session');if(!token)throw new AuthError('ابتدا وارد حساب شوید.',401);
 const {result,data}=await authRequest('user',undefined,token);
 if(!result.ok||!isAuthUser(data))throw new AuthError('نشست شما معتبر نیست. دوباره وارد شوید.',result.status>=500?503:401);
 const access=await requireAccountAccess(token,'login',data.id);return {token,userId:data.id,access};
}
