using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using HotUpdate.Gameplay;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
using Object=UnityEngine.Object;
/// Presentation routing/lifecycle tests and independent contact sheet; never writes an account.
public static class VerifyPartnerSkillFx
{
static string Repo=>Path.GetFullPath(Path.Combine(Application.dataPath,"../.."));
const string Folder="Doc/Design/Combat/PartnerSkills";
const string Bank="Assets/Bundles/Combat/BattleSkillPresentation.prefab";
static void Check(bool condition,string message){if(!condition)throw new Exception(message);}
static void Report(string name,object value){Directory.CreateDirectory(Path.Combine(Repo,Folder));File.WriteAllText(Path.Combine(Repo,Folder,name+".json"),JsonConvert.SerializeObject(value,Formatting.Indented));}
static ParticleSystem[] Roots(BattleSkillPresentation p)=>p.GetComponentsInChildren().Where(ps=>ps.transform.parent.GetComponent()==null).ToArray();
static Vector3 Point(int id)=>new Vector3(id<100?-2:2,1,(id<100?id%3:id%3)-1);
public static string Behavior()
{
Check(EditorApplication.isPlaying,"Use Play Mode");bool original=PrototypeClientPrefs.Sfx;var root=Object.Instantiate(AssetDatabase.LoadAssetAtPath(Bank));var p=root.GetComponent();
var config=JsonUtility.FromJson(File.ReadAllText(Path.Combine(Repo,"Config/Gameplay/greenfield.json")));
try
{
PrototypeClientPrefs.Sfx=true;Check(p.EffectKinds==15&&p.ProfileCount==10,"Missing profiles");int cast=1;
Action cue=(kind,pet,target,c)=>p.Play(new CombatEvent{kind=kind,source=pet,target=target,cast=c,skill="pet."+pet+".ability",arrival=10,expires=20},0,Point,config);
for(int pet=0;pet<10;pet++)
{
int c=cast++;cue("cast_start",pet,100,c);cue("release",pet,100,c);
cue(pet==0||pet==9?"shield":pet==2||pet==7?"heal":pet==4||pet==5?"slow":"hit",pet,pet==0||pet==2||pet==7||pet==9?pet:100,c);
cue("cast_end",pet,100,c);
}
cue("hit",4,100,cast++);cue("hit",0,100,cast++);
for(int i=5;i<15;i++)Check(p.EffectPlays[i]>0,"Unreached signature effect "+i);
for(int i=0;i<9;i++)Check(p.SoundPlays[i]>0,"Unreached sound "+i);
int sounds=p.SoundPlays[7];int chain=cast++;for(int target=100;target<103;target++)cue("hit",3,target,chain);Check(p.SoundPlays[7]==sounds+1,"Chain amplified lightning sound");
foreach(var line in root.GetComponentsInChildren().Where(l=>l.enabled))
{
Check(Vector3.Distance(line.transform.TransformPoint(line.GetPosition(0)),Point(3))<.001f||Vector3.Distance(line.transform.TransformPoint(line.GetPosition(0)),Point(8))<.001f,"Beam source detached");
Check(Enumerable.Range(100,3).Any(t=>Vector3.Distance(line.transform.TransformPoint(line.GetPosition(line.positionCount-1)),Point(t))<.001f),"Beam end detached");
}
int count=p.PlayedEffects; p.Play(new CombatEvent{kind="hit",source=3,target=100,cast=cast++,tick=0},1,Point,config);Check(p.PlayedEffects==count,"Stale catch-up replayed a hit");
// Recycle the four beam roots while earlier flash leases still exist.
for(int i=0;i<12;i++)cue("hit",3,100,cast++);Check(Roots(p).Length==76,"Pool expanded");p.Render(.3f,Point,_=>true);Check(root.GetComponentsInChildren().All(l=>!l.enabled),"Expired/recycled beam leaked");
cue("projectile",1,100,cast++);p.Render(.2f,Point,_=>true);Check(p.ActiveShots==1,"No projectile");var shot=Roots(p).Single(ps=>ps.isPlaying&&ps.name.Contains("Fireball"));Vector3 before=shot.transform.position;
p.SetPlayback(true,2);p.Render(.2f,Point,_=>true);Check(Vector3.Distance(before,shot.transform.position)<.001f&&shot.isPaused,"Pause moved projectile");Check(root.GetComponentsInChildren().All(ps=>Mathf.Approximately(ps.main.simulationSpeed,2)),"2x missed child emitter");p.SetPlayback(false,2);
p.Render(.3f,Point,_=>false);Check(p.ActiveShots==0,"Dead target retained projectile");
cue("death",0,0,cast++);Check(p.ActiveShields==1,"Death did not remove shield");cue("shield_expired",9,9,cast++);Check(p.ActiveShields==0,"Status expiry retained shield");
cue("cast_start",1,100,cast);Check(p.ActiveCharges==1,"No charge");cue("cancel",1,100,cast++);Check(p.ActiveCharges==0,"Cancel retained charge");
PrototypeClientPrefs.Sfx=false;sounds=p.PlayedSounds;cue("hit",3,100,cast++);Check(p.PlayedSounds==sounds&&root.GetComponentsInChildren().All(a=>!a.isPlaying),"Mute failed");PrototypeClientPrefs.Sfx=true;
int[] effects=p.EffectPlays.ToArray(),audio=p.SoundPlays.ToArray();cue("battle_end",0,0,0);Check(p.ActiveShots+p.ActiveCharges+p.ActiveShields+p.ActiveBeams==0&&root.GetComponentsInChildren().All(ps=>!ps.IsAlive())&&root.GetComponentsInChildren().All(a=>!a.isPlaying),"Battle end retained resources");
cue("shield",0,0,cast++);root.SetActive(false);Check(p.ActiveShields==0,"Disable retained shield");root.SetActive(true);
// Replay every exported server trace through the actual presentation consumer.
var rows=new List