# main.py
from fastapi import FastAPI, HTTPException, Depends
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
from beanie import PydanticObjectId
from typing import List, Optional
from pydantic import BaseModel, Field, EmailStr
from uuid import UUID
from bson.dbref import DBRef
import re

from database import init
from models import User, Booth, ScanRecord
from mission_models import MissionStatus, BoothScan

app = FastAPI()

@asynccontextmanager
async def lifespan(app: FastAPI):
    # 스타트업 로직
    await init()
    yield
    # 셧다운 로직 (필요 시 추가)

app = FastAPI(lifespan=lifespan, middleware=[])


# CORS 설정
origins = [
    "*",
    "http://ec2-3-36-108-191.ap-northeast-2.compute.amazonaws.com",
    "http://ec2-3-38-108-191.ap-northeast-2.compute.amazonaws.com:8000",
    "http://ec2-3-38-108-191.ap-northeast-2.compute.amazonaws.com:8001",

    # 필요한 경우 다른 도메인 추가
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,  # 허용할 도메인
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Pydantic 모델
class ScanRequest(BaseModel):
    phone_number: str = Field(..., pattern=r'^\d{10,15}$', unique=True)  # 유니크 인덱스 설정
    booth_id: Optional[UUID] = None  # Optional로 변경

class ScanResponse(BaseModel):
    success: bool
    message: Optional[str] = None
    detail: Optional[str] = None
    mission_status: MissionStatus
    
class BoothCreate(BaseModel):
    name: str
    qr_code: str

class LoginRequest(BaseModel):
    name: str
    email: EmailStr

class LoginResponse(BaseModel):
    message: str
    user_id: UUID
    phone_number: str

class RegisterRequest(BaseModel):
    name: str
    email: EmailStr
    company: Optional[str] = None
    position: Optional[str] = None
    phone_number: str = Field(..., pattern=r'^\d{10,15}$', unique=True)

class RegisterResponse(BaseModel):
    message: str
    user_id: UUID
    phone_number: str

class UserResponse(BaseModel):
    id: UUID
    name: str
    email: EmailStr
    company: Optional[str] = None
    position: Optional[str] = None
    phone_number: str

    
# 전화번호로 사용자 가져오기 또는 생성
async def get_or_create_user(phone_number: str) -> User:
    user = await User.find_one(User.phone_number == phone_number)
    if not user:
        user = User(phone_number=phone_number)
        await user.insert()
    return user

# 헬스 체크
@app.get("/")
async def healthcheck():
    return 200

# 사용자 등록 엔드포인트
@app.post("/register", response_model=RegisterResponse)
async def register(user: RegisterRequest):
    existing_user_email = await User.find_one(User.email == user.email)
    if existing_user_email:
        raise HTTPException(status_code=400, detail="이미 등록된 이메일입니다.")
    
    existing_user_phone = await User.find_one(User.phone_number == user.phone_number)
    if existing_user_phone:
        raise HTTPException(status_code=400, detail="이미 등록된 전화번호입니다.")
    
    new_user = User(
        name=user.name,
        email=user.email,
        company=user.company,
        position=user.position,
        phone_number=user.phone_number
    )
    await new_user.insert()
     
    return RegisterResponse(message="등록이 성공적으로 완료되었습니다.", user_id=new_user.id, phone_number=new_user.phone_number)

# 사용자 로그인 엔드포인트
@app.post("/login", response_model=LoginResponse)
async def login(login: LoginRequest):
    user = await User.find_one(User.email == login.email)
    if not user:
        raise HTTPException(status_code=400, detail="등록되지 않은 이메일입니다.")
    if user.name != login.name:
        raise HTTPException(status_code=400, detail="이름이 일치하지 않습니다.")
    
    return LoginResponse(message="로그인이 성공적으로 완료되었습니다.", user_id=user.id, phone_number=user.phone_number)
   

@app.get("/user/{user_id}", response_model=UserResponse)
async def get_user_info(user_id: UUID):
    user = await User.get(user_id)
    if not user:
        raise HTTPException(status_code=400, detail="등록되지 않은 사용자입니다.")
    
    return UserResponse(
        id=user.id,
        name=user.name,
        email=user.email,
        company=user.company,
        position=user.position,
        phone_number=user.phone_number
    )   
    
# QR 코드 스캔 및 미션 상태 반환
@app.post("/scan", response_model=ScanResponse)
async def scan_qr(scan: ScanRequest):
    # 사용자 가져오기 또는 생성
    user = await get_or_create_user(scan.phone_number)
    
    # QR 코드가 제공되지 않으면 스캔 기록만 조회
    if not scan.booth_id:
        mission_status = await get_mission_status(scan.phone_number)
        return ScanResponse(
            success=True,
            message="스캔 기록을 조회했습니다.",
            mission_status=mission_status
        )
    
    # 부스 찾기
    booth = await Booth.find_one(Booth.id == scan.booth_id)
    print(booth)
    if not booth:
        # 부스를 찾지 못했을 때 미션 상태 반환
        mission_status = await get_mission_status(scan.phone_number)
        return ScanResponse(
            success=False,
            detail="부스를 찾을 수 없습니다.",
            mission_status=mission_status
        )
    
    # DBRef 형식으로 user_id와 booth_id 생성
    user_ref = DBRef("users", user.id)
    booth_ref = DBRef("booths", booth.id)

    # 중복 스캔 방지
    existing_scan = await ScanRecord.find_one({
        "user_id": user_ref,
        "booth_id": booth_ref
    })
    print(existing_scan)
    if existing_scan:
        # 중복 스캔이라도 미션 상태를 조회하여 반환
        mission_status = await get_mission_status(scan.phone_number)
        print(mission_status)
        return ScanResponse(
            success=False,
            detail="Booth already scanned",
            mission_status=mission_status
        )
    
    # 스캔 기록 생성
    new_scan = ScanRecord(user_id=user, booth_id=booth)
    await new_scan.insert()
    
    # 미션 상태 조회
    mission_status = await get_mission_status(scan.phone_number)
    
    return ScanResponse(
        success=True,
        message="Booth scanned successfully",
        mission_status=mission_status
    )


# 미션 상태 조회
@app.get("/mission/{phone_number}", response_model=MissionStatus)
async def get_mission_status(phone_number: str):
    # 전화번호 형식 검증 (숫자만, 10~15자리)
    if not re.match(r'^\d{10,15}$', phone_number):
        raise HTTPException(status_code=400, detail="Invalid phone number format")
    
    user = await User.find_one(User.phone_number == phone_number)
    if not user:
        return MissionStatus(
            achieved=False,
            total_booths=await Booth.find().count(),
            scanned_booths=[]
        )
    
    print(user)
    # 전체 부스 수 계산
    total_booths = await Booth.find().count()
    # 사용자 스캔 기록 가져오기
    scan_records = await ScanRecord.find(ScanRecord.user_id.id == user.id).to_list()
    user_scans = len(scan_records)
    achieved = user_scans >= total_booths
    
    # 완료된 부스 정보 수집
    scanned_booths = []
    for record in scan_records:
        booth = await record.booth_id.fetch()  # Link 객체를 통해 부스 문서 가져오기
        if booth:
            scanned_booths.append(
                BoothScan(
                    booth_id=booth.id,
                    name=booth.name,
                    qr_code=booth.qr_code,
                    scanned_at=record.timestamp
                )
            )
    
    return MissionStatus(
        achieved=achieved,
        total_booths=total_booths,
        scanned_booths=scanned_booths
    )

# 부스 목록 조회 (관리자용 또는 클라이언트용)
@app.get("/booths", response_model=List[dict])
async def get_booths():
    booths = await Booth.find_all().to_list()
    return [{"id": str(booth.id), "name": booth.name, "qr_code": booth.qr_code} for booth in booths]

# 부스 추가 (관리자용)
@app.post("/booths", status_code=201)
async def create_booth(booth: BoothCreate):
    # 부스 이름 중복 확인
    existing_booth_name = await Booth.find_one(Booth.name == booth.name)
    if existing_booth_name:
        raise HTTPException(status_code=400, detail="Booth with given name already exists")
    
    # 부스 QR 코드 중복 확인
    existing_booth_qr = await Booth.find_one(Booth.qr_code == booth.qr_code)
    if existing_booth_qr:
        raise HTTPException(status_code=400, detail="Booth with given QR code already exists")
    
    # 부스 생성
    new_booth = Booth(name=booth.name, qr_code=booth.qr_code)
    await new_booth.insert()
    return {"id": str(new_booth.id), "name": new_booth.name, "qr_code": new_booth.qr_code}