package dev.privacyllc.period.designsystem.art

import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.PathEffect
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin

/**
 * Placeholder illustrations, exactly as PRODUCT_PLAN.md §42 asks for them.
 *
 * > If custom artwork cannot be produced immediately, create polished
 * > placeholder vector assets and keep them behind replaceable resource names.
 *
 * Every one is drawn, not drawn *on* — vector paths, no raster, so they stay
 * crisp at any density and add nothing to the APK.
 *
 * ## What none of them may be
 *
 * §42's forbidden list, and it is a product decision rather than squeamishness:
 * no blood drops, tampons, pads, uterus imagery, gender symbols or anatomical
 * graphics. This app is opened in public. Someone glancing over a shoulder
 * should learn nothing, and that is a property of the pictures as much as of the
 * notification text.
 *
 * So the whole visual language here is **overlapping circular forms** — a cycle,
 * abstractly. It is also the language of the launcher icon, which is what makes
 * a placeholder set look deliberate rather than improvised.
 *
 * ## Replacing them
 *
 * These are the replaceable names. When real artwork arrives, the drawing inside
 * each function changes and no call site does.
 */

/** Screen 1 of onboarding. Overlapping rings — the cycle, before it means anything specific. */
@Composable
fun WelcomeIllustration(
    primary: Color,
    accent: Color,
    modifier: Modifier = Modifier,
    size: Dp = IllustrationDefaults.Size,
) {
    Canvas(
        modifier
            .size(size)
            .semantics { contentDescription = "Three overlapping circles" },
    ) {
        val r = this.size.minDimension * 0.28f
        val c = center
        val stroke = Stroke(width = this.size.minDimension * 0.035f)
        drawCircle(accent.copy(alpha = 0.55f), r, Offset(c.x - r * 0.55f, c.y - r * 0.2f), style = stroke)
        drawCircle(accent.copy(alpha = 0.75f), r, Offset(c.x + r * 0.55f, c.y - r * 0.2f), style = stroke)
        drawCircle(primary, r, Offset(c.x, c.y + r * 0.45f), style = stroke)
    }
}

/**
 * The empty state — no periods logged yet.
 *
 * A ring with a single gap, because the story is *the cycle has not started
 * being recorded yet*, not *something is broken*. Empty states are one of the
 * two screens most people meet first and the one most often left undesigned.
 */
@Composable
fun EmptyStateIllustration(
    color: Color,
    modifier: Modifier = Modifier,
    size: Dp = IllustrationDefaults.Size,
) {
    Canvas(
        modifier
            .size(size)
            .semantics { contentDescription = "An empty circle" },
    ) {
        val d = this.size.minDimension * 0.72f
        val stroke = this.size.minDimension * 0.035f
        drawArc(
            color = color,
            startAngle = -60f,
            sweepAngle = 300f,
            useCenter = false,
            topLeft = Offset(center.x - d / 2f, center.y - d / 2f),
            size = Size(d, d),
            style = Stroke(
                width = stroke,
                pathEffect = PathEffect.dashPathEffect(floatArrayOf(d / 22f, d / 14f)),
            ),
        )
    }
}

/**
 * The learning state — a cycle or two recorded, not yet a pattern.
 *
 * §42: points gradually converging into a pattern. Scattered dots that tighten
 * toward a ring as they go round, which is the honest picture of what the engine
 * is doing and reads as progress rather than as an error.
 */
@Composable
fun LearningIllustration(
    color: Color,
    accent: Color,
    modifier: Modifier = Modifier,
    size: Dp = IllustrationDefaults.Size,
) {
    Canvas(
        modifier
            .size(size)
            .semantics { contentDescription = "Scattered dots settling into a circle" },
    ) {
        val radius = this.size.minDimension * 0.32f
        val dot = this.size.minDimension * 0.028f
        val count = 12
        repeat(count) { i ->
            val progress = i / (count - 1f)
            val angle = (progress * 1.6f * PI - PI / 2).toFloat()
            // Early points sit off the ring and late ones settle onto it.
            val scatter = (1f - progress) * radius * 0.42f
            val wobble = if (i % 2 == 0) scatter else -scatter
            val r = radius + wobble
            drawCircle(
                color = if (progress > 0.66f) color else accent.copy(alpha = 0.35f + progress * 0.5f),
                radius = dot * (0.7f + progress * 0.6f),
                center = Offset(center.x + r * cos(angle), center.y + r * sin(angle)),
            )
        }
    }
}

/**
 * The privacy promise screen.
 *
 * A shield with a circular cycle mark inside it — §42 asks for a minimal
 * shield or lock motif, and putting the cycle inside the shield is the whole
 * sentence the screen is making.
 */
@Composable
fun PrivacyIllustration(
    color: Color,
    accent: Color,
    modifier: Modifier = Modifier,
    size: Dp = IllustrationDefaults.Size,
) {
    Canvas(
        modifier
            .size(size)
            .semantics { contentDescription = "A shield containing a circle" },
    ) {
        val w = this.size.minDimension * 0.52f
        val h = this.size.minDimension * 0.62f
        val c = center
        val shield = Path().apply {
            moveTo(c.x, c.y - h / 2f)
            lineTo(c.x + w / 2f, c.y - h / 2f + h * 0.16f)
            cubicTo(
                c.x + w / 2f, c.y + h * 0.16f,
                c.x + w * 0.28f, c.y + h * 0.42f,
                c.x, c.y + h / 2f,
            )
            cubicTo(
                c.x - w * 0.28f, c.y + h * 0.42f,
                c.x - w / 2f, c.y + h * 0.16f,
                c.x - w / 2f, c.y - h / 2f + h * 0.16f,
            )
            close()
        }
        drawPath(shield, color, style = Stroke(width = this.size.minDimension * 0.035f))
        drawCircle(accent, radius = w * 0.20f, center = Offset(c.x, c.y - h * 0.02f), style = Stroke(width = this.size.minDimension * 0.03f))
        drawCircle(accent, radius = w * 0.05f, center = Offset(c.x, c.y - h * 0.02f - w * 0.20f))
    }
}

/**
 * The forecast mark used beside the hero number on Today.
 *
 * A ring with one offset dot — the same idea as the launcher icon, so the app's
 * own mark appears inside it rather than only on the home screen.
 */
@Composable
fun CycleProgressMark(
    color: Color,
    accent: Color,
    /** 0f at the start of the cycle, 1f at the predicted start of the next. */
    progress: Float,
    modifier: Modifier = Modifier,
    size: Dp = 56.dp,
) {
    Canvas(modifier.size(size)) {
        val r = this.size.minDimension * 0.38f
        val stroke = this.size.minDimension * 0.07f
        drawCircle(color.copy(alpha = 0.25f), r, center, style = Stroke(width = stroke))
        drawArc(
            color = color,
            startAngle = -90f,
            sweepAngle = 360f * progress.coerceIn(0f, 1f),
            useCenter = false,
            topLeft = Offset(center.x - r, center.y - r),
            size = Size(r * 2, r * 2),
            style = Stroke(width = stroke),
        )
        val angle = (-PI / 2 + 2 * PI * progress.coerceIn(0f, 1f)).toFloat()
        drawCircle(accent, stroke * 0.85f, Offset(center.x + r * cos(angle), center.y + r * sin(angle)))
    }
}

object IllustrationDefaults {
    val Size: Dp = 160.dp
}
