using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using SameBeat.Api.Hubs;
using SameBeat.Application.Interfejsy;
using SameBeat.Application.Komendy;
namespace SameBeat.Api.Controllers;
///
/// Sterowanie scena glowna przez hosta -- uklad i sloty (ETAP 3). Trasa zaczyna sie od "api/host",
/// wiec jest automatycznie chroniona [Authorize] przez KonwencjaAutoryzacjiHosta.
///
/// BEZPIECZENSTWO: kazda zmiana rozglaszana jest przez SignalR do WSZYSTKICH polaczonych klientow
/// (rowniez anonimowych widzow) -- payload to zawsze StanScenyDto (numerSlotu + nick), NIGDY
/// uczestnikId. Gdyby id wyciekl publicznie, dowolny widz moglby go uzyc do podszycia sie pod
/// przypisanego uczestnika przy POST /api/join/token-sceny.
///
[ApiController]
[Route("api/host/stage")]
public class HostStageController : ControllerBase
{
private readonly ISerwisSceny _serwisSceny;
private readonly IHubContext _hubKontekst;
public HostStageController(ISerwisSceny serwisSceny, IHubContext hubKontekst)
{
_serwisSceny = serwisSceny;
_hubKontekst = hubKontekst;
}
/// Biezacy stan sceny -- do wyswietlenia w panelu hosta po odswiezeniu strony.
[HttpGet]
public async Task PobierzStanAsync(CancellationToken token)
{
var wynik = await _serwisSceny.PobierzStanScenyAsync(token);
if (!wynik.Sukces || wynik.Dane is null)
{
return BadRequest(new { blad = wynik.Blad });
}
return Ok(wynik.Dane);
}
[HttpPost("layout")]
public async Task ZmienUkladAsync([FromBody] ZmienUkladScenyKomenda komenda, CancellationToken token)
{
var wynik = await _serwisSceny.ZmienUkladAsync(komenda.Uklad, token);
if (!wynik.Sukces || wynik.Dane is null)
{
return BadRequest(new { blad = wynik.Blad });
}
// Pelny stan po korekcie (sierocy sloty ponad nowy limit zostaly juz zwolnione) -- klient nie
// musi dociagac osobnego GET /api/host/stage, zeby zobaczyc efekt.
await _hubKontekst.Clients.All.SendAsync("UkladScenyZmieniony", wynik.Dane, cancellationToken: token);
return Ok(wynik.Dane);
}
[HttpPost("slots/{numerSlotu:int}/assign")]
public async Task PrzypiszSlotAsync(int numerSlotu, [FromBody] PrzypiszSlotKomenda komenda, CancellationToken token)
{
var wynik = await _serwisSceny.PrzypiszSlotAsync(numerSlotu, komenda.UczestnikId, token);
if (!wynik.Sukces || wynik.Dane is null)
{
return BadRequest(new { blad = wynik.Blad });
}
var przypisanySlot = wynik.Dane.Sloty.Find(s => s.NumerSlotu == numerSlotu);
if (przypisanySlot is not null)
{
await _hubKontekst.Clients.All.SendAsync(
"UczestnikDodanyDoSceny",
new { numerSlotu = przypisanySlot.NumerSlotu, nick = przypisanySlot.Nick, uklad = wynik.Dane.Uklad },
cancellationToken: token);
}
await _hubKontekst.Clients.All.SendAsync("ScenaZmieniona", wynik.Dane, cancellationToken: token);
return Ok(wynik.Dane);
}
///
/// Zwalnia slot -- uczestnik wraca do statusu Oczekuje.
///
/// ETAP 7: oprocz "miekkiego" usuniecia w bazie ponizej (SerwisSceny.ZwolnijSlotAsync), serwis TWARDO
/// wymusza to tez w LiveKit (ISerwisAdministracjiLiveKit.OdbierzUprawnieniaScenyAsync, best effort) --
/// uczestnik fizycznie traci canPublishSources (kamera+mikrofon), niezaleznie od tego, czy klient
/// w ogole obsluzy zdarzenie SignalR ponizej.
///
[HttpDelete("slots/{numerSlotu:int}")]
public async Task ZwolnijSlotAsync(int numerSlotu, CancellationToken token)
{
var wynik = await _serwisSceny.ZwolnijSlotAsync(numerSlotu, token);
if (!wynik.Sukces || wynik.Dane is null)
{
return BadRequest(new { blad = wynik.Blad });
}
await _hubKontekst.Clients.All.SendAsync(
"UczestnikUsunietyZeSceny",
new { numerSlotu, uklad = wynik.Dane.Uklad },
cancellationToken: token);
await _hubKontekst.Clients.All.SendAsync("ScenaZmieniona", wynik.Dane, cancellationToken: token);
return Ok(wynik.Dane);
}
}