import random
import string
from typing import Protocol

from pydantic.dataclasses import dataclass

from app.models.password import Password
from app.repositories.type import Repository


@dataclass(frozen=True)
class PassGenOptions:
    service: str
    seed: int
    length: int = 8
    symbols: bool = False
    numbers: bool = True


def generate_password(repo: Repository, options: PassGenOptions) -> str:
    if repo.exists(options.service):
        raise ValueError("password for this service has already been set")

    characters = _build_characters(symbols=options.symbols, numbers=options.numbers)
    random_generator = _new_random_generator(options.seed)
    password = "".join(random_generator.sample(characters, options.length))
    repo.save(options.service, password)
    return password


class RandomSampler(Protocol):
    def sample(self, population: str, k: int) -> list[str]:
        ...  # pragma nocover


def _new_random_generator(seed: int) -> RandomSampler:
    return random.Random(seed)


def _build_characters(symbols: bool, numbers: bool) -> str:
    return "".join(
        [
            string.ascii_letters,
            string.punctuation if symbols else "",
            string.digits if numbers else "",
        ]
    )


def list_all_saved_passwords(repo: Repository) -> list[Password]:
    return repo.list_all()

import random
import string
from typing import Protocol

from pydantic.dataclasses import dataclass

from app.models.password import Password
from app.repositories.type import Repository


@dataclass(frozen=True)
class PassGenOptions:
    service: str
    seed: int
    length: int = 8
    symbols: bool = False
    numbers: bool = True


def generate_password(repo: Repository, options: PassGenOptions) -> str:
    if repo.exists(options.service):
        raise ValueError("password for this service has already been set")

    characters = _build_characters(symbols=options.symbols, numbers=options.numbers)
    random_generator = _new_random_generator(options.seed)
    password = "".join(random_generator.sample(characters, options.length))
    repo.save(options.service, password)
    return password


class RandomSampler(Protocol):
    def sample(self, population: str, k: int) -> list[str]:
        ...  # pragma nocover


def _new_random_generator(seed: int) -> RandomSampler:
    return random.Random(seed)


def _build_characters(symbols: bool, numbers: bool) -> str:
    return "".join(
        [
            string.ascii_letters,
            string.punctuation if symbols else "",
            string.digits if numbers else "",
        ]
    )


def list_all_saved_passwords(repo: Repository) -> list[Password]:
    return repo.list_all()

# pragma nocover