package de.trainingstagebuch.app.repository

import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Test

class RepositoryMappersTest {

    @Test
    fun `mapClubsToHomeData maps count and names`() {
        val clubsJson = Json.parseToJsonElement(
            """
            [
              {"id":"1","name":"Club A"},
              {"id":"2","name":"Club B"},
              {"id":"3"}
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapClubsToHomeData(clubsJson)

        assertEquals(3, result.clubCount)
        assertEquals(listOf("Club A", "Club B"), result.clubNames)
    }

    @Test
    fun `parseErrorPayload parses backend error format`() {
        val error = RepositoryMappers.parseErrorPayload(
            """{"success":false,"code":"ERROR_UNAUTHORIZED","error":"Nicht autorisiert"}"""
        )

        assertNotNull(error)
        assertEquals("ERROR_UNAUTHORIZED", error?.code)
        assertEquals("Nicht autorisiert", error?.message)
    }

    @Test
    fun `parseErrorPayload returns null for invalid json`() {
        val error = RepositoryMappers.parseErrorPayload("not-json")
        assertNull(error)
    }

    @Test
    fun `mapMembersToMemberItems maps display names and flags`() {
        val membersJson = Json.parseToJsonElement(
            """
            [
              {"id":"1","firstName":"Max","lastName":"Mustermann","active":true,"testMembership":false},
              {"id":"2","firstName":"Eva","lastName":"Muster","active":false,"testMembership":true}
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapMembersToMemberItems(membersJson)

        assertEquals(2, result.size)
        assertEquals("Max", result[0].firstName)
        assertEquals("Mustermann", result[0].lastName)
        assertEquals("Max Mustermann", result[0].displayName)
        assertEquals(true, result[0].active)
        assertEquals(false, result[0].testMembership)
        assertEquals("Eva Muster", result[1].displayName)
        assertEquals(false, result[1].active)
        assertEquals(true, result[1].testMembership)
    }

    @Test
    fun `mapMemberGalleryImages filters by member and primary first`() {
        val galleryJson = Json.parseToJsonElement(
            """
            [
              {"imageId":"img-2","memberId":"2","url":"b.jpg","isPrimary":false},
              {"imageId":"img-1","memberId":"2","url":"a.jpg","isPrimary":true},
              {"imageId":"img-x","memberId":"3","url":"x.jpg","isPrimary":true}
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapMemberGalleryImages(galleryJson, memberId = "2")

        assertEquals(2, result.size)
        assertEquals("img-1", result[0].imageId)
        assertEquals(true, result[0].isPrimary)
        assertEquals("img-2", result[1].imageId)
    }

    @Test
    fun `mapMemberNotes maps notes with fallback fields`() {
        val notesJson = Json.parseToJsonElement(
            """
            [
              {"id":"n1","content":"Erste Notiz","createdAt":"2025-01-01"},
              {"_id":"n2","text":"Zweite Notiz"}
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapMemberNotes(notesJson)

        assertEquals(2, result.size)
        assertEquals("n1", result[0].id)
        assertEquals("Erste Notiz", result[0].content)
        assertEquals("n2", result[1].id)
        assertEquals("Zweite Notiz", result[1].content)
    }

    @Test
    fun `mapDiaryDates maps required diary fields`() {
        val diaryJson = Json.parseToJsonElement(
            """
            [
              {"id":"d1","date":"2026-03-01","trainingStart":"18:00","trainingEnd":"20:00","diaryTags":[{"id":"t1","name":"Kondition"}]},
              {"_id":"d2","date":"2026-03-02"}
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapDiaryDates(diaryJson)

        assertEquals(2, result.size)
        assertEquals("d1", result[0].id)
        assertEquals("2026-03-01", result[0].date)
        assertEquals("18:00", result[0].trainingStart)
        assertEquals("20:00", result[0].trainingEnd)
        assertEquals(1, result[0].tags.size)
        assertEquals("t1", result[0].tags[0].id)
        assertEquals("d2", result[1].id)
        assertEquals("2026-03-02", result[1].date)
    }

    @Test
    fun `mapDiaryParticipants maps participant and member ids`() {
        val participantsJson = Json.parseToJsonElement(
            """
            [
              {"id":"p1","memberId":"m1","groupId":"g1"},
              {"_id":"p2","memberId":"m2"}
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapDiaryParticipants(participantsJson)

        assertEquals(2, result.size)
        assertEquals("p1", result[0].id)
        assertEquals("m1", result[0].memberId)
        assertEquals("g1", result[0].groupId)
        assertEquals("p2", result[1].id)
        assertEquals("m2", result[1].memberId)
    }

    @Test
    fun `mapDiaryActivities maps id and description`() {
        val activitiesJson = Json.parseToJsonElement(
            """
            [
              {"id":"a1","description":"Aufschlagtraining"},
              {"_id":"a2","activity":"Beinarbeit"}
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapDiaryActivities(activitiesJson)

        assertEquals(2, result.size)
        assertEquals("a1", result[0].id)
        assertEquals("Aufschlagtraining", result[0].description)
        assertEquals("a2", result[1].id)
        assertEquals("Beinarbeit", result[1].description)
    }

    @Test
    fun `mapTags supports root and nested tag objects`() {
        val tagsJson = Json.parseToJsonElement(
            """
            [
              {"id":"t1","name":"Technik"},
              {"tag":{"id":"t2","label":"Ausdauer"}}
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapTags(tagsJson)

        assertEquals(2, result.size)
        assertEquals("t1", result[0].id)
        assertEquals("Technik", result[0].name)
        assertEquals("t2", result[1].id)
        assertEquals("Ausdauer", result[1].name)
    }

    @Test
    fun `mapDiaryPlanItems maps activity and timeblock`() {
        val planJson = Json.parseToJsonElement(
            """
            [
              {"id":"p1","activity":"Einspielen","isTimeblock":false},
              {"_id":"p2","isTimeblock":true}
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapDiaryPlanItems(planJson)

        assertEquals(2, result.size)
        assertEquals("p1", result[0].id)
        assertEquals("Einspielen", result[0].activity)
        assertEquals(false, result[0].isTimeblock)
        assertEquals("p2", result[1].id)
        assertEquals(true, result[1].isTimeblock)
    }

    @Test
    fun `mapDiaryGroups maps id and name`() {
        val groupsJson = Json.parseToJsonElement(
            """
            [
              {"id":"g1","name":"Gruppe A"},
              {"_id":"g2"}
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapDiaryGroups(groupsJson)

        assertEquals(2, result.size)
        assertEquals("g1", result[0].id)
        assertEquals("Gruppe A", result[0].name)
        assertEquals("g2", result[1].id)
    }

    @Test
    fun `mapParticipantIds maps assignment ids`() {
        val json = Json.parseToJsonElement(
            """
            [
              {"participantId":"p1"},
              {"participantId":"p2"},
              {"participantId":"p1"}
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapParticipantIds(json)

        assertEquals(setOf("p1", "p2"), result)
    }

    @Test
    fun `mapPredefinedActivities maps id name and optional fields`() {
        val json = Json.parseToJsonElement(
            """
            [
              {"id":"a1","name":"Topspin VH","code":"TS-VH","duration":"20","durationText":"4x5"},
              {"_id":"a2","name":"Aufschlag"}
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapPredefinedActivities(json)

        assertEquals(2, result.size)
        assertEquals("a1", result[0].id)
        assertEquals("Topspin VH", result[0].name)
        assertEquals("TS-VH", result[0].code)
        assertEquals("20", result[0].duration)
        assertEquals("4x5", result[0].durationText)
        assertEquals("a2", result[1].id)
        assertEquals("Aufschlag", result[1].name)
    }

    @Test
    fun `mapPredefinedActivity maps detail including images`() {
        val json = Json.parseToJsonElement(
            """
            {
              "id":"a1",
              "name":"Topspin",
              "code":"TS",
              "description":"Topspin Uebung",
              "duration":"20",
              "durationText":"4x5",
              "imageLink":"https://example.com/image.png",
              "drawingData":{"strokes":[1,2]},
              "images":[{"id":"img1","imageLink":"https://example.com/img1.png","drawingData":{"x":1}}]
            }
            """.trimIndent()
        ).jsonObject

        val result = RepositoryMappers.mapPredefinedActivity(json)

        assertNotNull(result)
        assertEquals("a1", result?.id)
        assertEquals("Topspin", result?.name)
        assertEquals("TS", result?.code)
        assertEquals("Topspin Uebung", result?.description)
        assertEquals(1, result?.images?.size)
        assertEquals("img1", result?.images?.firstOrNull()?.imageId)
    }

    @Test
    fun `mapTrainingStats maps members and days`() {
        val json = Json.parseToJsonElement(
            """
            {
              "members":[
                {"id":"m1","firstName":"Max","lastName":"Muster","participation12Months":10,"participation3Months":3,"participationTotal":25}
              ],
              "trainingsCount12Months":40,
              "trainingsCount3Months":12,
              "trainingDays":[
                {"id":"d1","date":"2026-02-01","participantCount":14}
              ]
            }
            """.trimIndent()
        ).jsonObject

        val result = RepositoryMappers.mapTrainingStats(json)

        assertEquals(1, result.members.size)
        assertEquals("m1", result.members[0].id)
        assertEquals(40, result.trainingsCount12Months)
        assertEquals(12, result.trainingsCount3Months)
        assertEquals(1, result.trainingDays.size)
        assertEquals(14, result.trainingDays[0].participantCount)
    }

    @Test
    fun `mapMemberTransferConfig maps login credentials and defaults`() {
        val configJson = Json.parseToJsonElement(
            """
            {
              "id":"cfg-1",
              "server":"https://example.org",
              "loginEndpoint":"/auth/login",
              "loginCredentials":{"username":"u1","password":"p1","tenant":"abc"},
              "transferEndpoint":"/transfer",
              "transferTemplate":"{\"id\":\"{{id}}\"}",
              "useBulkMode":true
            }
            """.trimIndent()
        ).let { it as kotlinx.serialization.json.JsonObject }

        val result = RepositoryMappers.mapMemberTransferConfig(configJson)

        assertEquals("cfg-1", result.id)
        assertEquals("https://example.org", result.server)
        assertEquals("/auth/login", result.loginEndpoint)
        assertEquals("json", result.loginFormat)
        assertEquals("u1", result.loginUsername)
        assertEquals("tenant:abc", result.loginAdditionalField1)
        assertEquals("/transfer", result.transferEndpoint)
        assertEquals("POST", result.transferMethod)
        assertEquals("json", result.transferFormat)
        assertEquals(true, result.useBulkMode)
    }

    @Test
    fun `mapMemberTransferSummary supports numeric and string counts`() {
        val summaryJson = Json.parseToJsonElement(
            """
            {
              "success": false,
              "message": "teilweise fehlgeschlagen",
              "transferred": 2,
              "total": "4",
              "invalidMembers": [
                {"member":{"firstName":"Max","lastName":"Mustermann"}},
                {"member":{"id":"m-2"}}
              ],
              "errors": [
                {"member":"m-2","error":"timeout"},
                "generic error"
              ]
            }
            """.trimIndent()
        ).let { it as kotlinx.serialization.json.JsonObject }

        val result = RepositoryMappers.mapMemberTransferSummary(summaryJson)

        assertEquals(false, result.success)
        assertEquals("teilweise fehlgeschlagen", result.message)
        assertEquals(2, result.transferred)
        assertEquals(4, result.total)
        assertEquals(listOf("Max Mustermann", "m-2"), result.invalidMembers)
        assertEquals(listOf("m-2: timeout", "generic error"), result.errors)
    }

    @Test
    fun `mapScheduleMatches maps teams result and code`() {
        val json = Json.parseToJsonElement(
            """
            [
              {
                "id":"m1",
                "date":"2026-03-01",
                "time":"18:30:00",
                "homeTeam":{"name":"Club A I"},
                "guestTeam":{"name":"Club B I"},
                "isCompleted":true,
                "homeMatchPoints":9,
                "guestMatchPoints":4,
                "code":"ABCD12"
              }
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapScheduleMatches(json)

        assertEquals(1, result.size)
        assertEquals("m1", result[0].id)
        assertEquals("Club A I", result[0].homeTeamName)
        assertEquals("Club B I", result[0].guestTeamName)
        assertEquals(true, result[0].isCompleted)
        assertEquals("ABCD12", result[0].code)
    }

    @Test
    fun `mapTournamentMatches maps round players and result`() {
        val json = Json.parseToJsonElement(
            """
            [
              {
                "id":"tm1",
                "round":"Finale",
                "player1":{"member":{"firstName":"Max","lastName":"Muster"}},
                "player2":{"member":{"firstName":"Eva","lastName":"Muster"}},
                "result":"3:1",
                "isFinished":true
              }
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapTournamentMatches(json)

        assertEquals(1, result.size)
        assertEquals("tm1", result[0].id)
        assertEquals("Finale", result[0].round)
        assertEquals("Max Muster", result[0].player1Name)
        assertEquals("Eva Muster", result[0].player2Name)
        assertEquals("3:1", result[0].result)
        assertEquals(true, result[0].isFinished)
    }

    @Test
    fun `mapPermissionMembers maps role flags and nested user`() {
        val json = Json.parseToJsonElement(
            """
            [
              {
                "userId":"u1",
                "role":"admin",
                "approved":true,
                "isOwner":false,
                "user":{"email":"a@example.org","firstName":"Anna","lastName":"Admin"},
                "permissions":{"members":{"write":true}},
                "effectivePermissions":{"members":{"write":true,"read":true}}
              }
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapPermissionMembers(json)

        assertEquals(1, result.size)
        assertEquals("u1", result[0].userId)
        assertEquals("admin", result[0].role)
        assertEquals(true, result[0].approved)
        assertEquals("a@example.org", result[0].email)
        assertEquals(true, result[0].permissions["members"]?.get("write"))
    }

    @Test
    fun `mapClubTeams maps league and season details`() {
        val json = Json.parseToJsonElement(
            """
            [
              {
                "id":"t1",
                "name":"Herren 1",
                "league":{"id":"l1","name":"Bezirksliga","association":"WTTV","groupname":"G1","myTischtennisGroupId":"100"},
                "season":{"id":"s1","season":"2025/2026"},
                "myTischtennisTeamId":"200"
              }
            ]
            """.trimIndent()
        )

        val result = RepositoryMappers.mapClubTeams(json)

        assertEquals(1, result.size)
        assertEquals("t1", result[0].id)
        assertEquals("l1", result[0].leagueId)
        assertEquals("Bezirksliga", result[0].leagueName)
        assertEquals("s1", result[0].seasonId)
        assertEquals("2025/2026", result[0].seasonName)
        assertEquals("200", result[0].myTischtennisTeamId)
    }

    @Test
    fun `mapLogsPage maps wrapped logs payload`() {
        val json = Json.parseToJsonElement(
            """
            {
              "success": true,
              "data": {
                "total": 2,
                "logs": [
                  {"id":"l1","method":"GET","path":"/api/test","logType":"api_request","statusCode":200},
                  {"id":"l2","method":"POST","path":"/api/other","logType":"manual","statusCode":500}
                ]
              }
            }
            """.trimIndent()
        )

        val result = RepositoryMappers.mapLogsPage(json)

        assertEquals(2, result.total)
        assertEquals(2, result.logs.size)
        assertEquals("l1", result.logs[0].id)
        assertEquals(500, result.logs[1].statusCode)
    }

    @Test
    fun `mapMyTischtennisAccount maps account object inside wrapper`() {
        val json = Json.parseToJsonElement(
            """
            {
              "account": {
                "email": "user@example.com",
                "clubId": "club-1",
                "clubName": "Club One",
                "fedNickname": "HE",
                "lastLoginSuccess": "2026-03-01T10:00:00Z"
              }
            }
            """.trimIndent()
        )

        val result = RepositoryMappers.mapMyTischtennisAccount(json)

        assertNotNull(result)
        assertEquals("user@example.com", result?.email)
        assertEquals("club-1", result?.clubId)
        assertEquals("Club One", result?.clubName)
        assertEquals("HE", result?.fedNickname)
    }

    @Test
    fun `mapOfficialTournamentDetail maps competitions and participation`() {
        val json = Json.parseToJsonElement(
            """
            {
              "id":"ot-1",
              "parsedData":{
                "title":"BZM 2026",
                "termin":"01.05.2026",
                "competitions":[
                  {"id":1,"ageClassCompetition":"Herren A","startTime":"10:00","entryFee":"10 EUR"}
                ]
              },
              "participation":[
                {"competitionId":1,"memberId":"42","wants":true,"registered":false,"participated":false,"placement":null}
              ]
            }
            """.trimIndent()
        )

        val result = RepositoryMappers.mapOfficialTournamentDetail(json)

        assertNotNull(result)
        assertEquals("ot-1", result?.id)
        assertEquals("BZM 2026", result?.title)
        assertEquals(1, result?.competitions?.size)
        assertEquals("Herren A", result?.competitions?.firstOrNull()?.name)
        assertEquals(1, result?.participation?.size)
        assertEquals("42", result?.participation?.firstOrNull()?.memberId)
        assertEquals(true, result?.participation?.firstOrNull()?.wants)
    }
}
