#ifndef SERVER_SETUP_ROUTE
#define SERVER_SETUP_ROUTE
#include "network.h"
#include "player.h"
#include "TextViewport.h"

char* jsonBuffer = nullptr;
using PlaylistHandler = std::function<void(JsonArray)>;

void handleNotFound( AsyncWebServerRequest *request ){
	String message = "Not Found!\r\n\r\n";
	message += "Endpoint: ";
	message += request->url();
	message += "\r\n";
	request->send( 404, "text/plain", message );
}

auto handlePlaylistUpload = [](
	AsyncWebServerRequest *request,
    uint8_t *data,
    size_t len,
    size_t index,
    size_t total,
    PlaylistHandler handler
	)
{
		// 🔹 pierwszy chunk → alokacja
		if( index == 0 ){

				if( jsonBuffer ){
					free( jsonBuffer );
					jsonBuffer = nullptr;
				}

			jsonBuffer = (char*)ps_malloc( total + 1 );

				if( !jsonBuffer ){
					request->send(500, "text/plain", "Błąd alokowania PSRAM dla bufora");
					return;
				}
		}

    // kopiowanie chunków
    memcpy( jsonBuffer + index, data, len );

		// 🔹 ostatni chunk
		if( index + len == total ){
			jsonBuffer[total] = '\0';
			SpiRamJsonDocument doc( total * 1.5 );
			DeserializationError err = deserializeJson( doc, jsonBuffer );

				if( err ){
					String msg = "Błąd parsowania JSON-a: ";
					msg += err.c_str();
					request->send( 400, "text/plain", msg );
					free( jsonBuffer );
					jsonBuffer = nullptr;
					return;
				}

			JsonArray arr = doc.as<JsonArray>();
			// 👉 TU WYWOŁUJEMY KONKRETNĄ AKCJĘ
			handler( arr );
			request->send(200, "text/plain", "OK");
			free( jsonBuffer );
			jsonBuffer = nullptr;
		}
};

void server_setupRoute( AsyncWebServer* server ){
	
    server->on( "/", HTTP_GET, []( AsyncWebServerRequest *request ){
	
			if( !LittleFS.exists( HTML_INDEX_GZFILE ) ){
				request->send( 404, "text/plain", "File not found" );
				return;
			}
		
		AsyncWebServerResponse *response = request->beginResponse( LittleFS, HTML_INDEX_GZFILE, "text/html" );
        response->addHeader( "Content-Encoding", "gzip" );
		response->addHeader( "Cache-Control", "no-cache" );
        request->send( response );
    });
	
    server->on( "/test.html", HTTP_GET, []( AsyncWebServerRequest *request ){
		
			if( !LittleFS.exists( HTML_INDEX_FILE ) ){
				request->send( 404, "text/plain", "File not found" );
				return;
			}

		AsyncWebServerResponse *response = request->beginResponse( LittleFS, HTML_INDEX_FILE, "text/html" );
		response->addHeader( "Cache-Control", "no-cache" );
		request->send( response );	
    });
	
    server->on( maincfg.getLogoIMG().c_str(), HTTP_GET, []( AsyncWebServerRequest *request ){
		String filename = maincfg.getLogoIMG().c_str();
			
			if( !LittleFS.exists( filename ) ){
				request->send( 404, "text/plain", "File not found" );
				return;
			}
			
		AsyncWebServerResponse *response = request->beginResponse( 
			LittleFS,
			filename.c_str(),
			getContentType( filename )
		);

		request->send( response );
    });

	server->on( "/api/config", HTTP_GET, [](AsyncWebServerRequest *request ){

		File f = LittleFS.open( CONFIG_FILE, "r" );

			if( !f ){
				request->send( 404, "text/plain", "File not found" );
				return;
			}

		AsyncWebServerResponse *response = request->beginResponse( f, "application/json" );
		response->addHeader( "Cache-Control", "no-cache" );
		request->send( response );
	});
	
    server->on( "/api/netlist", HTTP_GET, [](AsyncWebServerRequest *request ){
		    
			if( !LittleFS.exists( NETLIST_FILE ) ){
				request->send( 404, "text/plain", "File not found" );
				return;
			}
        
		request->send( LittleFS, NETLIST_FILE, "application/json" );
    });

	server->on("/api/play", HTTP_POST, [](AsyncWebServerRequest * request) {

			if( !request->hasArg("url") ){
				request->send(400, "text/plain", "Bad request! Missing url");
				return;
			}

		String url = request->arg("url");

			if( url.length() == 0 ){
				request->send( 400, "text/plain", "URL is empty" );
				return;
			}

		player.stopSong();
		player.connecttohost(url.c_str());
		request->send( 200, "text/plain", "OK" );
	});
	
	server->on("/api/stop", HTTP_GET, [](AsyncWebServerRequest * request) {
		player.stopSong();
		request->send( 200, "text/plain", "OK" );
	});
	
	server->on("/api/resume", HTTP_GET, [](AsyncWebServerRequest * request) {

			if( !player.resumePlay() ){
				request->send( 400, "text/plain", "Last played URL is empty!" );
				return;
			}

		request->send( 200, "text/plain", "OK" );
	});
	
	server->on("/api/next", HTTP_GET, [](AsyncWebServerRequest * request) {
		player.nextTrack( 0 );
		request->send( 200, "text/plain", "OK" );
	});
	
	server->on("/api/prev", HTTP_GET, [](AsyncWebServerRequest * request) {
		player.prevTrack( 0 );
		request->send( 200, "text/plain", "OK" );
	});
	
	server->on("/api/set", HTTP_GET, [](AsyncWebServerRequest * request) {
			
			if( request->hasArg("n") ){
				int n = request->arg("n").toInt()-1;
				player.setTrack( 0, n );
			}
			
		request->send( 200, "text/plain", "OK" );
	});
/*	
    server->on( "/api/playlist", HTTP_GET, [](AsyncWebServerRequest *request ){ 
		AsyncResponseStream *response = request->beginResponseStream( "application/json" );
		player.serializePlaylist( *response );
		request->send( response );
		
		xTaskCreate([](void*){
			AsyncResponseStream *response = request->beginResponseStream( "application/json" );
			player.serializePlaylist( *response );
			request->send( response );
		}, "playlistSerialize", 2048, NULL, 1, NULL);
    });



server->on("/api/playlist", HTTP_GET, [](AsyncWebServerRequest *request){

    const Track* playlist = player.getPlaylist();
    size_t size = player.getPlaylistSize();

    AsyncResponseStream *response = request->beginResponseStream("application/json");

    // 📦 dobierz rozmiar
    DynamicJsonDocument doc(
        JSON_ARRAY_SIZE(size) + size * JSON_OBJECT_SIZE(1) + 200
    );

    JsonArray arr = doc.to<JsonArray>();

    for (size_t i = 0; i < size; i++) {
        JsonObject obj = arr.createNestedObject();
        obj["url"] = playlist[i].url ? playlist[i].url : "";
    }

    serializeJson(doc, *response);  // 🔥 bezpośrednio do streamu
    request->send(response);
});
*/

    server->on( "/api/cplaylist", HTTP_GET, [](AsyncWebServerRequest *request ){
		AsyncResponseStream *response = request->beginResponseStream("application/json");
		player.serializePlaylist( *response );
		request->send( response );
    });
	
    server->on( "/api/playlist", HTTP_GET, [](AsyncWebServerRequest *request ){
		    
			if( !LittleFS.exists( PLAYLIST_FILE ) ){
				request->send( 404, "text/plain", "File not found" );
				return;
			}
        
		request->send( LittleFS, PLAYLIST_FILE, "application/json" );
    });

	server->on(
		"/api/playlist",
		HTTP_POST,
		[](AsyncWebServerRequest *request){},
		NULL,
		[](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){
			bool edit = false;

				if (request->hasParam("edit")) {
					edit = true;
				}

			handlePlaylistUpload(request, data, len, index, total,
				[edit](JsonArray arr){
					player.loadPlaylist(arr, edit);
				}
			);
		}
	);
	
	server->on(
		"/api/playlist_append",
		HTTP_POST,
		[]( AsyncWebServerRequest *request ){},
		NULL,
		[]( AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total ){
			handlePlaylistUpload( request, data, len, index, total,
				[]( JsonArray arr ){
					player.appendToPlaylist( arr );
				}
			);
		}
	);
	
    server->on( "/api/save_playlist", HTTP_GET, [](AsyncWebServerRequest *request ){
		player.savePlaylistToFile( request );
    });
	
    server->on(
		"/api/config",
		HTTP_POST,
        [](AsyncWebServerRequest *request){},
        NULL,
        [](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)
		{

			// Sprawdzenie maksymalnego rozmiaru
			if( total > MAX_CONFIG_SIZE ){
				request->send( 413, "text/plain", "Rozmiar pliku przekracza limit." );
				return;
			}

			// Pierwszy fragment - otwieramy plik tymczasowy
			if( index == 0 ){
				File *f = new File( LittleFS.open( TEMP_CONFIG_FILE, "w" ) );
					
					if( !*f ){
						request->send( 500, "text/plain", "Nie można utworzyć pliku tymczasowego." );
						delete f;
						return;
					}
						
				request->_tempObject = f;
			}

        // Dopisanie fragmentu do pliku
        File *f = (File*)request->_tempObject;
            
			if( f ) f->write(data, len);

			// Ostatni fragment
			if( index + len == total ){
				f->close();
				delete f;
				request->_tempObject = nullptr;

				// Wczytanie pliku tymczasowego do Stringa
				File tempF = LittleFS.open(TEMP_CONFIG_FILE, "r");
					
					if( !tempF ){
						request->send( 500, "text/plain", "Błąd odczytu pliku tymczasowego." );
						return;
					}

				String jsonString;
					
					while( tempF.available() ) jsonString += (char)tempF.read();
					
				tempF.close();

					// Walidacja JSON i typów pól
					if( !isValidJSON( jsonString ) ){
						LittleFS.remove( TEMP_CONFIG_FILE );
						request->send( 400, "text/plain", "Niepoprawny JSON lub typy pól." );
						return;
					}

				// Atomowa zamiana pliku
				LittleFS.remove( CONFIG_FILE );
				LittleFS.rename( TEMP_CONFIG_FILE, CONFIG_FILE );
				request->send(200, "text/plain", "OK");
				// Przeładowanie konfiguracji w pamięci
				config.hotReload();
			}
        }
    );
	
	server->on("/api/restart", HTTP_POST, [](AsyncWebServerRequest *request){
		request->send( 200, "text/plain", "Restarting" );
		xTaskCreate([](void*){
			vTaskDelay( pdMS_TO_TICKS( 500 ) );
			WiFi.disconnect( true, true );
			ESP.restart();
		}, "rebootTask", 2048, NULL, 1, NULL);
	});
	
	server->on( "/api/status", HTTP_GET, [](AsyncWebServerRequest *request ){
		int rssi = WiFi.RSSI();
		String wifiLevel = "ok";
		
			if(rssi < -75) wifiLevel = "warning";
			if(rssi < -85) wifiLevel = "critical";

		float cpuTemp = temperatureRead();

		String tempLevel = "ok";
		
			if(cpuTemp > 65) tempLevel = "warning";
			if(cpuTemp > 80) tempLevel = "critical";
/*
		uint32_t heap = ESP.getFreeHeap() / 1024;
		size_t fsTotal = LittleFS.totalBytes() / 1024;
		size_t fsUsed  = LittleFS.usedBytes() / 1024;

		uint32_t uptime = millis() / 1000;
		uint32_t h = uptime / 3600;
		uint32_t m = (uptime % 3600) / 60;
		uint32_t s = uptime % 60;
		char uptimeStr[20];
		sprintf( uptimeStr, "%02lu:%02lu:%02lu", h, m, s );
*/		
		bool playing = player.isRunning();
		uint32_t totalTime = 0;
		uint32_t currentSecond = 0;
		
			if( playing ){
				totalTime = player.getAudioFileDuration();
		
					if( totalTime ){
						currentSecond = player.getPlayingTime_s();
					}
			}
			
		uint32_t heapFree = ESP.getFreeHeap();
		uint32_t heapTotal = ESP.getHeapSize();	
		uint32_t psramFree = ESP.getFreePsram();
		uint32_t psramTotal = ESP.getPsramSize();	
			
		SpiRamJsonDocument doc( 1024 );
		doc["title"] = TitleText.getText();
		doc["info"] = InfoText.getText();
		doc["playing"] = playing;
		doc["playing_url"] = player.getPlayingURL();
		doc["trackmode"] = player.isTrackMode();
		doc["tracknum"] = player.getTrackNumbers();
		doc["codec"] = player.getCodecname();
		doc["sample_rate"] = player.getSampleRate();
		doc["total_time"] = totalTime;
		doc["current_time"] = currentSecond;
		doc["bits"] = player.getBitsPerSample();
		doc["bitrate"] = player.getBitRate();

		JsonObject wifi = doc.createNestedObject("wifi_rssi");
		wifi["value"] = rssi;
		wifi["unit"] = "dBm";
		wifi["level"] = wifiLevel;

		JsonObject cpu = doc.createNestedObject("cpu_temp");
		cpu["value"] = cpuTemp;
		cpu["unit"] = "°C";
		cpu["level"] = tempLevel;
		doc["sram"] = String( heapFree )+"/"+String( heapTotal );
		doc["psram"] = String( psramFree )+"/"+String( psramTotal );

		AsyncResponseStream *response = request->beginResponseStream("application/json");
		serializeJson( doc, *response );
		request->send( response );
	});
	
	server->onNotFound( handleNotFound );

}
#endif